2.4 Bean的作用域
Bean的作用域——指的是通过Spring容器工厂创建的对象作用范围;我们可以通过在bean标签中添加scope属性设置bean的作用域
- scope=“singleton” 表示设置当前bean为单例模式 (默认是饿汉式,也就是说在Spring容器初始化时就会完成对象的创建;当在bean标签中设置lazy-init="true"变为懒汉式)
- scope=“prototype” 表示设置当前bean为非单例模式,每次从Spring容器获取对象都会创建一个新的对象
Scope | Description |
---|---|
singleton | 单例模式,从创建之后一直存在于Spring容器 |
prototype | 创建的实例不会存入Spring容器,每次获取都会创建新的对象 |
request | 作用于一次web请求,在一次请求中多次从Spring容器获取的是相同的实例 |
session | 作用于一次web会话,在一次会话中多次从Spring容器获取的是相同的实例 |
application | 作用于服务器的生命周期 |
websocket | 作用于一个网络连接 |
-
单例饿汉式
<bean id="student" class="com.qfedu.entity.Student"></bean> <bean id="student" class="com.qfedu.entity.Student" scope="singleton"></bean>
-
单例懒汉式
<bean id="student" class="com.qfedu.entity.Student" scope="singleton" lazy-init="true"></bean>
-
非单例模式:每次访问都是新对象
<bean id="student" class="com.qfedu.entity.Student" scope="prototype"></bean>
2.5 Bean的生命周期方法
Bean的生命周期方法——bean对象在创建或销毁的时候绑定执行的方法
- 在bean标签中通过init-method属性指定当前bean的初始化方法,通过destroy-method属性指定bean的销毁方法
-
在Bean类中定义初始化方法和销毁方法(无参无返回值)
public class Student { public void aaa(){ System.out.println("~~~~init"); } public void bbb(){ System.out.println("~~~~destory"); } }
-
在spring配置文件的bean标签中配置初始化方法和销毁方法
<bean id="student" class="com.qfedu.entity.Student" init-method="aaa" destroy-method="bbb" scope="singleton" ></bean>
2.6 自动装配
自动装配:Spring容器在实例化当前bean的时候可以自动从Spring容器中寻找
类型匹配
或者ID匹配
的实例进行赋值
- byType:根据当前Bean属性的类型在Spring容器中寻找匹配的实例;如果找不到则不赋值,如果找到多个则抛出异常;
- byName:根据当前Bean属性的属性名在Spring容器中寻找id与当前属性名相同的实例;如果找不到则不赋值,如果找到了但类型不匹配则抛出异常;
-
byType
<bean id="userDAOImpl" class="com.qfedu.dao.impl.UserDAOImpl"></bean> <bean id="userService" class="com....UserServiceImpl" autowire="byType"></bean>
-
byName
<bean id="userDAO" class="com.qfedu.dao.impl.UserDAOImpl"></bean> <bean id="userDAOImpl2" class="com.qfedu.dao.impl.UserDAOImpl2"></bean> <bean id="userService" class="com....UserServiceImpl" autowire="byName"></bean>
2.7 Spring IoC工作原理
三、Spring IoC 注解配置
Spring IoC的使用,需要通过xml将类声明给Spring容器进行管理,从而通过Spring工厂完成对象创建及属性值的注入;
Spring除了提供基于XML的配置方式外,还提供了基于注解的配置方式——将类交给Spring容器管理可以不用配置XML文件,直接在类上添加对应的注解即可。
3.1 Spring框架部署
3.1.1 创建Maven工程
略
3.1.2 添加SpringIoC依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.15</version>
</dependency>
3.1.3 创建Spring配置文件
-
在resources中创建Spring配置文件
applicationContext.xml
-
在配置文件中引入context命名空间
-
在配置文件中声明使用注解配置:
<?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 http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 声明使用注解配置 --> <context:annotation-config/> <!-- 声明Spring工厂注解的扫描范围 --> <context:component-scan base-package="com.qfedu.entity"/> </beans>
3.2 Spring IoC常用注解
3.2.1 @Component
- 类注解,声明此类被Spring容器管理,相当于XML配置的bean标签的功能
@Component(value = "book")
value属性相当于bean标签的id属性,指定此类在Spring容器的唯一ID;value属性可以省略,如果省略则id默认为类名首字母改小写
- 除了@Component注解声明类被Spring管理之外,@Service、@Controller、@Respository这三个注解都可以声明类被Spring容器管理
- @Controller 声明控制器类被Spring容器管理,例如Servlet
- @Service 声明业务逻辑类被Spring容器管理,例如UserServiceImpl、BookServiceImpl
- @Respository 声明持久层类被Spring容器管理,例如UserDAO、BookDAO
- @Component 声明一般类被Spring容器管理
3.2.2 @Scope
- 类注解,用于声明当前类的作用域,相当于bean标签的scope属性
- @Scope(“prototype”) 表示声明当前类为非单例模式(默认是单例模式)
3.2.3 @Lazy
- 类注解,声明一个单例模式的bean为懒汉式
- @Lazy(true) 声明当前单例模式的bean为懒汉式(默认是饿汉式)
3.2.4 @PostConstruct 和 @PreDestroy
- @PostConstruct 方法注解,声明此方法为初始化方法,在创建对象调用构造器后执行
- @PreDestroy 方法注解,声明此方法为实例销毁方法,在实例被GC回收之前执行
3.2.5 @Autowired
-
属性注解、set方法注解,设置当前属性自动装配,匹配规则:首先根据byName的规则从Spring容器查询id与当前属性名相同的bean进行装配,如果没有找到或者找到的bean与当前属性类型不匹配,再根据byType的规则从Spring容器查询类型匹配的bean进行装填;如果还是没有找到但是@Autowired(required=true)则抛出异常,如果@Autowired(required=false)则设置为null;如果根据类型找到多个则跑出异常
@Autowired private UserDAO userDAO;
-
这时我们可以通过
@Qualifier
声明装配的bean的id
@Autowired
public void setUserDAO(@Qualifier("userDAOImpl2") UserDAO userDAO) {
this.userDAO = userDAO;
}
3.2.6 @Resource
- 属性注解,作用与@Autowired相当;
- 首先根据byName的规则从Spring容器查询id与当前属性名相同的bean进行装配,如果没有找到再根据byType的规则从Spring容器查询类型匹配的bean进行装填;如果根据ID找到的bean与当前属性类型不匹配则抛出异常
四、代理设计模式
4.1 生活中代理
4.2 静态代理
使用代理的好处:
- 被代理类中只用关注核心业务的实现,将通用的管理型逻辑(事务管理、日志管理)和业务逻辑分离
- 将通用的代码放在代理类中进行实现,提高了代码的复用性
- 通过在代理类中添加业务逻辑,可以实现不改变原有代码的情况下实现业务增强
4.3 动态代理
动态代理——几乎可以为所有的类产生代理对象
动态代理的实现方式有2种
- JDK动态代理:是通过实现
被代理类实现的接口
创建代理对象的,JDK动态代理要求被代理类必须实现接口- CGLib动态代理:是通过继承被代理类创建代理对象的,CGLib不能为final类产生代理对象
4.3.1 JDK动态代理
1.创建JDK动态代理类
/**
* @Description JDK动态代理类
* JDK自带动态代理需要实现最少一个接口
* JDK动态代理类创建规则:
* 1.实现InvocationHandler接口,同时实现invoke方法
* 2.在类中定义一个Object类型变量obj,提供有参构造器,用于将被代理对象传递进来
* 3.定义一个getProxy方法返回代理对象
*/
public class JDKDynamicProxy implements InvocationHandler {
//被代理对象
private Object obj;
//有参构造器
public JDKDynamicProxy(Object obj) {
this.obj = obj;
}
public Object getProxy(){
ClassLoader classLoader = obj.getClass().getClassLoader();
Class<?>[] interfaces = obj.getClass().getInterfaces();
Object proxy = Proxy.newProxyInstance(classLoader,interfaces,this);
return proxy;
}
/**
* 通过此JDK动态代理类产生的代理对象,调用方法时都会执行当前invoke方法
* @param proxy
* @param method 指代的是通过代理对象调用的方法(insert)
* @param args 指代的是通过被代理对象调用的方法的参数 (insert方法的参数)
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("=============start");
Object r = method.invoke(obj, args); //执行insert方法
System.out.println("=============commit");
return r;
}
}
2.测试JDK动态代理
-
创建接口
public interface ProductDAO { public int insertProduct(); }
-
创建被代理类(使用JDK动态代理的被代理类要实现接口)
public class ProductDAOImpl implements ProductDAO { public int insertProduct() { System.out.println("添加商品"); return 0; } }
-
测试
public class TestJDK { public static void main(String[] args) { //JDK动态代理只能为实现了接口的类产生代理对象 ProductDAOImpl productDAO = new ProductDAOImpl();// 被代理对象 //通过JDK动态代理类创建被代理对象的代理对象(代理对象强转成被代理对象实现的接口类型) //proxy 代理对象 ProductDAO proxy = (ProductDAO) new JDKDynamicProxy(productDAO).getProxy(); //使用代理对象调用方法,并不是直接调用被代理类中方法, // 而是执行产生代理对象时指定的InvocationHandler的invoke方法 // 同时将调用的方法作为参数传递给invoke int i = proxy.insertProduct(); } }
通过拦截器拦截insert方法,跳转到invoke()
4.3.2 CGLib动态代理
1.创建CGLib动态代理类
-
添加CGlib依赖
<!-- https://mvnrepository.com/artifact/cglib/cglib --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>
-
创建CGLib动态代理类
/** * @Description CGLib动态代理 * @Author 千锋涛哥 * 公众号: Java架构栈 * * CGLib动态代理类创建规则 * 1.实现MethodInterceptor接口,实现intercept方法 * 2.在类中定义一个Object类型变量obj,提供有参构造器,用于将被代理对象传递进来 * 3.定义一个getProxy方法返回代理对象 * */ public class CGLibDynamicProxy implements MethodInterceptor { private Object obj; public CGLibDynamicProxy(Object obj) { this.obj = obj; } public Object getProxy(){ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(obj.getClass()); //如果使用此动态代理类产生的代理对象调用方法,则回调此类中intercept enhancer.setCallback(this); Object proxy = enhancer.create(); return proxy; } /** * 使用此动态代理类产生的代理对象调用方法时,并不会执行被调用的方法 * 而是执行此intercept方法,被调用的方法作为参数传递进来 * @param o * @param method 被调用的方法 * @param objects 被调用的方法的参数 * @param methodProxy * @return * @throws Throwable */ public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println(">>>>>>>>>>>>>>start"); Object r = method.invoke(obj, objects); System.out.println(">>>>>>>>>>>>>>commit"); return r; } }
2.测试CGLib动态代理
-
创建被代理类
public class TypeDAOImpl { public int insertType(){ System.out.println("insert type"); return 0; } }
-
测试
public class TestCGLib { public static void main(String[] args) { //被代理对象 TypeDAOImpl typeDAO = new TypeDAOImpl(); //通过CGLib产生代理对象——代理对象强转成被代理对象类型 TypeDAOImpl proxy = (TypeDAOImpl) new CGLibDynamicProxy(typeDAO).getProxy(); //通过代理对象调用方法 proxy.insertType(); } }
bject r = method.invoke(obj, objects);
System.out.println(">>>>>>>>>>>>>>commit");
return r;
}
}
**2.测试CGLib动态代理**
- 创建被代理类
```java
public class TypeDAOImpl {
public int insertType(){
System.out.println("insert type");
return 0;
}
}
-
测试
public class TestCGLib { public static void main(String[] args) { //被代理对象 TypeDAOImpl typeDAO = new TypeDAOImpl(); //通过CGLib产生代理对象——代理对象强转成被代理对象类型 TypeDAOImpl proxy = (TypeDAOImpl) new CGLibDynamicProxy(typeDAO).getProxy(); //通过代理对象调用方法 proxy.insertType(); } }