1. Spring简介
1.1 什么是Spring
Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。
-
目的:解决企业应用开发的复杂性
-
功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能
-
范围:任何Java应用
Spring是一个轻量级控制反转(**IoC)**和面向切面(AOP)的容器框架。
1.2 三层架构
- 表示层
- 业务逻辑层
- 数据访问层
Controller
Service
Dao
1.3 Spring 程序
Maven导入依赖
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>comg.lu</groupId>
<artifactId>spring-easy</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<!-- 导入 spring 框架 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
</dependencies>
</project>
ApplicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
IOC :把对象控制权 交给 Spring
<bean id="accountService" class="com.lu.service.impl.AccountServiceImpl"/>
<bean id="accountDao" class="com.lu.dao.impl.AccountDaoImpl" />
注入 accountService
和 accountDao
对象
读取配置
通过 ClassPathXmlApplicationContext
读取配置文件然后获取对象
ApplicationContext ac = new ClassPathXmlApplicationContext("ApplicationContext.xml");
这里的 ac 就是 sprng 核心容器
因此 我们可以通过 ac 来 获取对象
获取对象
这里以 IAccountService
为例 进行 获取对象
别名
<!-- 对象 起别名 -->
<alias name="user" alias="myUser"/>
bean配置
- id
唯一标识符
- class
对象全限定名:报名+类型
- name
别名(可以同时取多个别名)
<bean id="user" class="com.lu.pojo.User" name="u,user2">
<constructor-arg type="java.lang.String" value="luzhenfang"/>
</bean>
- import
导入配置文件
- 导入多个配置文件 合并为 一个 (一般用于团队开发)
<!-- 导入 -->
<import resource="xxxx.xml"/>
<import resource="xxxx.xml"/>
<import resource="xxxx.xml"/>
<import resource="xxxx.xml"/>
1.4 依赖注入
Class:Student
package com.lu.pojo;
import java.util.*;
public class Student {
private String name;
private Address address;
private String [] books;
private List<String> hobys;
private Map<String,String> card;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", address=" + address.getAddress() +
", books=" + Arrays.toString(books) +
", hobys=" + hobys +
", card=" + card +
", games=" + games +
", wife='" + wife + '\'' +
", info=" + info +
'}';
}
private Set<String> games;
private String wife;
private Properties info;
public Properties getInfo() {
return info;
}
public void setInfo(Properties info) {
this.info = info;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String[] getBooks() {
return books;
}
public void setBooks(String[] books) {
this.books = books;
}
public List<String> getHobys() {
return hobys;
}
public void setHobys(List<String> hobys) {
this.hobys = hobys;
}
public Map<String, String> getCard() {
return card;
}
public void setCard(Map<String, String> card) {
this.card = card;
}
public Set<String> getGames() {
return games;
}
public void setGames(Set<String> games) {
this.games = games;
}
public String getWife() {
return wife;
}
public void setWife(String wife) {
this.wife = wife;
}
}
Class:Address
package com.lu.pojo;
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
Resoruces:Complex.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="address" class="com.lu.pojo.Address">
<property name="address" value="ShanXi"/>
</bean>
<!-- DI 注入 复杂类型 -->
<bean id="student" class="com.lu.pojo.Student">
<!-- 普通注入 -->
<property name="name" value="luzhenfang"/>
<!-- bean 注入 ref -->
<property name="address" ref="address"/>
<!-- 数组注入 -->
<property name="books">
<array>
<value>Java程序设计</value>
<value>C#程序设计</value>
<value>Python3爬虫实战</value>
<value>C++ Primer</value>
<value>算法导论</value>
<value>大话数据结构</value>
</array>
</property>
<!-- list 注入 -->
<property name="hobys">
<list>
<value>听歌</value>
<value>写BUG</value>
<value>补BUG</value>
<value>瞎折腾</value>
</list>
</property>
<!-- map 注入 -->
<property name="card">
<map>
<entry key="QQ" value="1318659507"/>
<entry key="Wechat" value="lzfxxxx"/>
</map>
</property>
<!-- set 注入 -->
<property name="games">
<set>
<value>CS:GO</value>
<value>Human:Fail</value>
</set>
</property>
<!-- null pointer 注入 -->
<property name="wife">
<null/>
</property>
<!-- properties 注入 -->
<property name="info">
<props>
<prop key="学号">1822032023</prop>
<prop key="性别">男</prop>
</props>
</property>
</bean>
</beans>
普通 注入
<bean id="address" class="com.lu.pojo.Address">
<property name="address" value="ShanXi"/>
</bean>
bean 注入
<bean id="student" class="com.lu.pojo.Student">
<property name="address" ref="address"/>
</bean>
数组 注入
<bean id="student" class="com.lu.pojo.Student">
<property name="books">
<array>
<value>Java程序设计</value>
<value>C#程序设计</value>
<value>Python3爬虫实战</value>
<value>C++ Primer</value>
<value>算法导论</value>
<value>大话数据结构</value>
</array>
</property>
</bean>
list 注入
<bean id="student" class="com.lu.pojo.Student">
<property name="books">
<list>
<value>听歌</value>
<value>写BUG</value>
<value>补BUG</value>
<value>瞎折腾</value>
</list>
</property>
</bean>
map 注入
<bean id="student" class="com.lu.pojo.Student">
<property name="card">
<map>
<entry key="QQ" value="1318659507"/>
<entry key="Wechat" value="lzfxxxx"/>
</map>
</property>
</bean>
set 注入
<bean id="student" class="com.lu.pojo.Student">
<property name="games">
<set>
<value>CS:GO</value>
<value>Human:Fail</value>
</set>
</property>
</bean>
NullPointer 注入
<bean id="student" class="com.lu.pojo.Student">
<property name="wife">
<null/>
</property>
</bean>
1.5 命名空间
P nameSpace
property
属性注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- p 命名空就注入 property 注入 -->
<bean id="user" class="com.lu.pojo.User" p:name="luzhenfang" p:age="20"/>
</beans>
C nameSpace
construct-args
构造器注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- c 命名空间 注入 construct-args -->
<bean id="user2" class="com.lu.pojo.User" c:name="lu" c:age="20"/>
</beans>
1.6 Bean作用域
Scope | Description | 解释 |
---|---|---|
singleton | (Default) Scopes a single bean definition to a single object instance for each Spring IoC container. | 单例 |
prototype | Scopes a single bean definition to any number of object instances. | 原型 |
request | Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext . | |
session | Scopes a single bean definition to the lifecycle of an HTTP Session . Only valid in the context of a web-aware Spring ApplicationContext . | |
application | Scopes a single bean definition to the lifecycle of a ServletContext . Only valid in the context of a web-aware Spring ApplicationContext . | |
websocket | Scopes a single bean definition to the lifecycle of a WebSocket . Only valid in the context of a web-aware Spring ApplicationContext . |
单例模式
单例模式:无论创建多少次 都只有一个实例
原型模式
原型模式:每次从容器中get 时候,都会产生一个新的对象.
<!-- 单例 -->
<bean id ="singleUser" class="com.lu.pojo.User" scope="singleton"/>
<!-- 原型 -->
<bean id ="protoUser" class="com.lu.pojo.User" scope="prototype"/>
1.7 自动装配
在Spring中 有3种装配的方式
- 在 xml 中 显式配置
- 在 java 种 显式配置
- 隐式 自动装配 ★
ByName
会自动在容器上下文中查找,和自己对象set方法后面的值对应的 beanid
ByType
会自动在容器上下文中查找,和自己对象属性类型相同的 bean
byName 时候,需要保证所有bean id 唯一,并且这个bean 需要和自动注入的属性set 方法的值一致!
byName 时候,需要保证所有class 唯一,并且这个bean 需要和自动注入的属的类型一致!
byName:前提:id 唯一
byType 前提:对象 唯一
2. 注解
The introduction of annotation-based configuration raised the question of whether this approach is “better” than XML. The short answer is “it depends.”
要使用注解的话需要 导入注解依赖
然后开启注解支持
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
xsi:schemaLocation=http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
>
<!-- 开启注解支持 -->
<context:annotation-config/>
</beans>
@Autowired
直接在属性上使用即可,也可以在set 方法上使用
- required 属性为 false 说明这个对象可以为 null,否则不允许为空
@Nullable
自动标记这个注解 说明这个字段可以为 null
@Qualifier
- value
指定对象id
当出现二义性时 指定 匹配对象 (和@Autowired在一块使用)
例如:
<bean id="dog" class="com.lu.pojo.animal.impl.ImplDog"/>
<bean id="dog2" class="com.lu.pojo.animal.impl.ImplDog"/>
@Autowired
@Qualifier(value = "dog2")
private ImplDog dog;
@Component
把这个类交给Spring管理 --> 等同于
<bean id ="xx" class ="xxx"/>
衍生注解
- dao
@Repository
- service
@Service
- controller
@Controller
这几个 注解功能都是一样的:将某个类注册
到Spring 容器中装配bean
@Value
给属性注入值 -- > 等同于
<property name="xxx" value ="xxx"/>
也可以放到 set 方法上面
@Scope
- value
Spring 作用域
例如:
@Scope("singleton")
推荐
- xml 管理bean
- 注解 负责属性的注入
3.1 JavaConfig
使用java 类配置完全替代掉 xml 配置
@Configuration
代表这是一个配置类
等同于 beans.xml
@ComponentScan
告诉Spring 从哪里 找Bean
由你来定义哪些包需要被扫描。一旦你指定了,Spring将会将在被指定的包及其下级包(sub packages)中寻找bean.
@Bean
注册一个 bean ,相当于 之前写的 bean 标签
这个方法的名字 就相当于 bean 标签中的 id
方法返回值 就相当于 bean 标签种的class 属性
DEMO
ClassConfig.java
package com.lu.pojo.config;
import com.lu.pojo.config.component.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.lu.pojo.config.component")
public class AppConfig {
@Bean
public User user(){
return new User();
}
}
/**
Configuration注解来告诉spring 这是一个配置类
ComponentScan 告诉spring 扫描范围 如果不写 默认 为 当前 所在包
Bean 注册一个 bean 名字 就是要 用 的 id
return: 返回一个 new 后的 对象
*/
User.java
package com.lu.pojo.config.component;
import org.springframework.beans.factory.annotation.Value;
public class User {
@Value("luzhenfang")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/**
这里就是一个简单的 set 注入
使用 value 注解 实现值的注入
*/
ConfigClassTest.java
import com.lu.pojo.config.AppConfig;
import com.lu.pojo.config.component.User;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class ConfigTest {
@Test
public void Test(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
User user = (User) context.getBean("user");
System.out.println(user.getName());
}
}
/**
AnnotationConfigApplicationContext 是 spring 注解 装载后的容器
*/
4. 代理模式
4.1 静态代理
静态代理模式:这种模式可以实现帮助被代理者完成一些前期的准备工作和后期的善后工作,但是核心的业务逻辑仍然是由被代理者完成。
代码结构由三部分组成
(1)接口:主题
(2)代理类
(3)被代理类
实现方式:代理类和被代理类要实现同一个主题接口,而且代理类中要有一个被代理类的属性(target),这样才能把核心业务逻辑交还给被代理类完成;而一些与核心业务逻辑无关的逻辑,并且需求是多变的,那么这些逻辑就可以交给代理类来完成
UserService.java
package com.lu.pojo.proxyPattery;
/**
* 业务接口
* 增删改查
*
*/
public interface UserService {
void select();
void add();
void delete();
void alter();
}
UserServiceImpl.java
package com.lu.pojo.proxyPattery;
public class UserServiceImpl implements UserService {
public void select() {
}
public void add() {
}
public void delete() {
}
public void alter() {
}
}
UserServiceProxy.java
package com.lu.pojo.proxyPattery;
public class UserServiceProxy implements UserService{
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
public void select() {
log("查询数据");
userService.select();
}
public void add() {
log("添加数据");
userService.add();
}
public void delete() {
log("删除数据");
userService.delete();
}
public void alter() {
log("修改数据");
userService.alter();
}
public void log(String str){
System.out.println("[DEBUG]:"+str);
}
}
ProxyTest.java
package com.lu.pojo.proxyPattery;
import org.junit.Test;
public class ProxyTest {
@Test
public void Test(){
/**
* 代理模式测试
* 创建一个 被代理对象 UserService
* 然后创建一个 代理对象
* 通过 set 注入 让 代理对象 去代理 UserService
* 然后 实现解耦
*/
UserServiceImpl service = new UserServiceImpl();
UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(service);
proxy.add();
proxy.select();
}
}
代理模式好处:
- 可以使真实角色操作更加纯粹
- 公共交给代理角色 实现业务分工
- 公共业务发生扩展时 方便集中管理
缺点:
- 一个真实角色 就会产生一个代理角色;代码量翻倍,开发效率变低.
4.2 动态代理
- 动态代理和静态代理角色一样
- 动态代理类是动态生成的,不用我们直接写
分类
- 基于接口的动态代理 --- JDK动态代理
- 基于类的动态代理 ---cglib
landlord.java
package com.lu.pojo.proxyInvocation;
public interface Landlord {
void rent();
}
LandlordImpl.java
package com.lu.pojo.proxyInvocation;
public class LandlordImpl implements Landlord {
public void rent() {
System.out.println("房租租房");
}
}
ProxyInvocationHandler.java
package com.lu.pojo.proxyInvocation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandler implements InvocationHandler {
private LandlordImpl landlord;
public void setLandlord(LandlordImpl landlord) {
this.landlord = landlord;
}
// 返回代理对象
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),landlord.getClass().getInterfaces(),this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
Object result = method.invoke(landlord,args);
return result;
}
public void seeHouse(){
System.out.println("中介看房");
}
public void log(String log){
System.out.println("[DEBUG]: "+log);
}
}
InvocationTest.java
import com.lu.pojo.proxyInvocation.Landlord;
import com.lu.pojo.proxyInvocation.LandlordImpl;
import com.lu.pojo.proxyInvocation.ProxyInvocationHandler;
import org.junit.Test;
public class InvocationTest {
@Test
public void Test(){
// 真实角色
LandlordImpl landlord = new LandlordImpl();
// 代理角色
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setLandlord(landlord);
Landlord proxy = (Landlord) pih.getProxy();
proxy.rent();
}
}
5. AOP
5.1 原生SpringAPI实现
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 注册 bean -->
<bean id="userService" class="com.lu.pojo.AOP.UserServiceImpl"/>
<bean id="log" class="com.lu.pojo.AOP.log.Log"/>
<bean id="afterLog" class="com.lu.pojo.AOP.log.AfterLog"/>
<!-- 配置 AOP -->
<!-- 原生 api 接口 -->
<aop:config>
<!-- 切入点 expression 表达式 execution (要执行的 位置 * * *) -->
<aop:pointcut id="pointcut" expression="execution(* com.lu.pojo.AOP.UserServiceImpl.*(..))"/>
<!-- 执行 环绕增加 -->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
Log.java
package com.lu.pojo.AOP.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class Log implements MethodBeforeAdvice {
// method 要执行的目标对象方法
// objects 参数
// target 目标对象
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName()+"的"+method.getName()+"被执行了");
}
}
5.2 自定义类实现
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 注册 bean -->
<bean id="userService" class="com.lu.pojo.AOP.UserServiceImpl"/>
<bean id="log" class="com.lu.pojo.AOP.log.Log"/>
<bean id="afterLog" class="com.lu.pojo.AOP.log.AfterLog"/>
<!-- 自定义 -->
<bean id="diy" class="com.lu.pojo.AOP.diy.PointCut"/>
<aop:config>
<!-- 要引用的 类 -->
<aop:aspect ref="diy">
<!-- 切入点 -->
<aop:pointcut id="point" expression="execution(* com.lu.pojo.AOP.UserServiceImpl.*(..))"/>
<!-- 通知 -->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
</beans>
pointCut.java
package com.lu.pojo.AOP.diy;
public class PointCut {
public void before(){
System.out.println("=====方法执行前=====");
}
public void after(){
System.out.println("=====方法执行后=====");
}
}
5.3 注解实现
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 注册 bean -->
<bean id="userService" class="com.lu.pojo.AOP.UserServiceImpl"/>
<bean id="log" class="com.lu.pojo.AOP.log.Log"/>
<bean id="afterLog" class="com.lu.pojo.AOP.log.AfterLog"/>
<bean id="annoPointCut" class="com.lu.pojo.AOP.annotation.PointCut"/>
<!-- 开启注解支持 -->
<aop:aspectj-autoproxy/>
</beans>
pointCut.java
package com.lu.pojo.AOP.annotation;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
// 标注 这个类 是一个切面
@Aspect
public class PointCut {
@Before("execution(* com.lu.pojo.AOP.UserServiceImpl.*(..))")
public void before(){
System.out.println("AnnotationPointCut:方法执行前");
}
@After("execution(* com.lu.pojo.AOP.UserServiceImpl.*(..))")
public void after(){
System.out.println("AnnotationPointCut:方法执行后");
}
}