Spring 工厂创建复杂对象的3种方式
1、FactoryBean接口
- 实现
FactoryBean
接口
// 返回需要创建的对象
public Object getObject() throws Exception {
return null;
}
// 返回当前创建对象的Class对象
public Class<?> getObjectType() {
return null;
}
// 是否单例
public boolean isSingleton() {
return false;
}
- Spring 配置
<bean id="connection" class="com.lovesosa.spring5.factory.MyFactoryBean"/>
如果配置的bean是 FactoryBean
接口的实现类,那么创建出来的对象是在 FactoryBean
getObject() 返回的对象。
- 如何获取自定义工厂类的对象?
MyFactoryBean factoryBean = (MyFactoryBean) ioc.getBean("&connection");
2、实例工厂
不用实现Spring FactoryBean
接口,无侵入
<bean id="myFactoryBean" class="com.lovesosa.spring5.factory.MyFactoryBean"/>
<bean id="conn" factory-bean="myFactoryBean" factory-method="getBean"/>
3、静态工厂
与 实例工厂的差别在于方法是静态方法
<bean id="myFactoryBean" class="com.lovesosa.spring5.factory.MyFactoryBean" factory-method="静态方法名"/>
对象的生命周期
生命周期的三个阶段
- 创建阶段
- 初始化阶段
- 销毁阶段
1.创建阶段
Spring工厂何时创建对象?
- scope=“singleton”:Spring工厂创建的同时,对象创建
- scope=“prototype”:Spring工厂会在获取对象的时候创建对象
2.初始化
- Spring工厂在创建完对象后,调用对象的初始化方法,完成对应的初始化操作。
- 初始化方法:用户提供,Spring调用
方式一:实现 InitializingBean
接口的 afterPropertiesSet()
方法完成初始化操作
方式二:在对象中提供一个普通方法,通过配置文件的init-method=""
指定即可。
3.销毁阶段
Spring在销毁对象前,会调用对象的销毁方法。
- Sprng什么时候销毁对象?在容器关闭前销毁。
ioc.close()
- 用户自己提供方法,Spring调用
方式一:实现DisposableBean
接口 的 destroy()
方式二:与初始化一样,destroy-method=""
注意:销毁方法的操作只适用于 单例Bean。
自定义类型转换器
- 实现
Converter
接口
public class MyConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try {
date = sdf.parse(source);
return date;
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
- 注册
Converter
到 Spring 中
<bean id="myConverter" class="com.lovesosa.spring5.MyConverter"/>
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="myConverter"/>
</set>
</property>
</bean>
<bean id="person" class="com.lovesosa.spring5.bean.Person">
<property name="name" value="tom"/>
<property name="age" value="3"/>
<property name="bir" value="2000-01-02"/>
</bean>
BeanPostProcessor 后置处理Bean
- 实现
BeanPostProcessor
接口
// 在容器创建好对象后被调用
default Object postProcessBeforeInitialization(Object bean, String beanName);
// 在对象初始化后被调用
default Object postProcessAfterInitialization(Object bean, String beanName);
- 配置
<bean id="myBeanPostProcessor" class="com.lovesosa.spring5.factory.MyBeanPostProcessor"/>
BeanPostProcessor 会对容器中创建的所有对象进行加工。
Spring的动态代理开发
引入AOP相关依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.14.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.3</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.8</version>
</dependency>
- 创建目标对象并注入到容器中
- 实现
MethodBeforeAdvice
接口,定义额外功能,并将其注入到Spring容器中
public class Before implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("---MethodBeforeAdvice log---");
}
}
- 定义切入点,额外功能加入的位置
<aop:config>
<aop:pointcut id="myPointcut" expression="execution(* *(..))"/>
</aop:config>
- 组装切入点和额外功能
<aop:advisor advice-ref="before" pointcut-ref="myPointcut"/>
- 调用,获得Spring创建的动态代理对象,并进行调用(在Spring中,获得原始对象,其实就是代理对象)
PersonService personService = (PersonService) ioc.getBean("personService");
personService.login("admin","123");
personService.register(new Person());
输出
******************************** log ***********************************
register()..............
******************************** log ***********************************
login()..............
Spring创建的动态代理类在哪?
Spring 框架在运行时,通过动态字节码技术,在JVM中创建的,运行在JVM内部,等程序结束后,会和JVM一起消失
1、MethodBeforeAdvice
/**
method:目标方法
args:目标方法参数
target:目标对象
*/
void before(Method method, Object[] args, @Nullable Object target)
2、MethodInterceptor(方法拦截器)
MethodInterceptor
接口:额外功能可以根据需要运行在原始方法的前、后、前后
public class Around implements MethodInterceptor {
/**
* 额外功能运行在目标方法的前后
*
* @param invocation 获得目标方法
* @return 目标方法的返回值
* @throws Throwable
*/
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("before----------------");
Object result = invocation.proceed();// 运行目标方法
System.out.println("after----------------");
return result;
}
}
MethodInterceptor
可以影响目标方法的返回值。
3、切入点
切入点决定额外功能加入的位置
<aop:pointcut id=“myPointcut” expression=“execution(* *(…))”/>
- execution():切入点函数
-
* *(..):切入点表达式
3.1、切入点表达式
* *(..):匹配所有方法
*:修饰符和返回值
*:方法名
():参数列表
..:任意参数
注意:在指定参数的时候,如果是非java.lang 包下的类,需要写全限定类名
精准的切点表达式
忽略包
# 类只存在一级包
* *.类名.*(..)
# 类存在多级包
* *..类名.*(..)
# 切入点包中的所有类,必须在proxy中,不包含子包
* com.lovesosa.proxy.*.*(..)
# 包含子包
* com.lovesosa.proxy..*.*(..)
###### 3.2、切入点函数
切入点函数:用于执行切入点表达式
-
execution
- 执行 方法 类 包切入点表达式
-
args
- 作用:主要用于方法参数的匹配,只关注方法参数
- 例如:args(String,String)
-
within
- 作用:主要用于进行类、包切入点表达式的匹配
- 例如:within(*…类名) within(com.lovesosa.proxy… * )
-
@annotation
- 作用:为具有特殊注解的方法加入额外功能
- 例如:@annotation(注解)
3.3、切入点函数的逻辑运算
整合多个切入点函数一起配合工作,进而完成更为复杂的需求
- and与操作,不能用于同种类型的切入点函数
execution(* login(..)) and args(String,String)
- or或操作
AOP
1、AOP编程的概念
AOP (Aspect Oriented Programing) 面向切面编程
AOP的概念:本质就是Spring 的动态代理开发,通过代理类为原始类增加额外功能。
好处:利于原始类的维护。
2、AOP编程的开发步骤
- 原始对象
- 额外功能 (MethodInterceptor)
- 切入点
- 组装切面(额外功能 + 切入点)
3、切面的概念
切面 = 切入点 + 额外功能
AOP 底层实现原理
1.核心问题
- AOP如何创建动态代理类(动态字节码技术)
- Spring 容器是如何加工创建代理对象的。
2.动态代理类的创建
2.1 JDK的动态代理
Proxy.newInstance()
/**
ClassLoader:通过类加载器创建代理类的Class对象
interfaces:原始对象实现的接口
InvocationHandler:添加额外功能的接口
*/
public class TestJDKProxy {
public static void main(String[] args) {
// 1.创建原始对象
PersonService personService = new PersonServiceImpl();
// 2.JDK创建动态代理
Class<? extends PersonService> clazz = personService.getClass();
PersonService personServiceProxy = (PersonService) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), (p, m, a) -> {
System.out.println("before");
Object result = m.invoke(personService, a);
System.out.println("after");
return result;
});
boolean result = personServiceProxy.login("admin", "admin");
System.out.println(result);
}
}
2.2 CGlib的动态代理
- CGlib创建动态代理的原理:父子继承关系创建代理对象,原始类作为父类,代理类作为子类,这样即保证2者方法一致,同时在代理类中提供新的实现(额外功能+原始方法)
// 1.创建原始对象
UserService userService = new UserService();
// 2.通过CGlib的方式创建动态代理对象
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(TestCGlib.class.getClassLoader());
enhancer.setSuperclass(userService.getClass());
MethodInterceptor interceptor = (o, m, a, p) -> {
System.out.println("***********cglib日志");
return m.invoke(userService,a);
};
enhancer.setCallback(interceptor);
UserService userServiceProxy = (UserService) enhancer.create();
userServiceProxy.login("admin", "123456");
对比两者创建动态代理的方式
- JDK动态代理:Proxy.newProxyInstance() 通过接口创建代理的实现类
- CGlib动态代理:Enhancer 通过继承父类创建代理类
3.在看BenaPostProcessor
public class ProxyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return Proxy.newProxyInstance(ProxyBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), (p, m, a) -> {
System.out.println("--- log ---");
return m.invoke(bean,a);
});
}
}
<bean id="userService" class="com.lovesosa.spring5.factory.UserServiceImpl"/>
<bean id="postProcessor" class="com.lovesosa.spring5.factory.ProxyBeanPostProcessor"/>
4. 基于注解的AOP编程
定义切面类
@Aspect
public class MyAspect {
@Around(value = "execution(* com.lovesosa.spring5.aspect..UserServiceImpl.*(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("前置通知");
Object ret = joinPoint.proceed();
return ret;
}
}
<bean id="userService" class="com.lovesosa.spring5.aspect.UserServiceImpl"/>
<bean id="around" class="com.lovesosa.spring5.aspect.MyAspect"/>
<aop:aspectj-autoproxy/>
切入点复用
@Pointcut("execution(* com.lovesosa.spring5.aspect..UserServiceImpl.*(..))")
public void myPointcut() {}
@Around("myPointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("前置通知");
Object ret = joinPoint.proceed();
return ret;
}
Spring对于JDK 与 CGlib 的切换
默认情况下,AOP编程底层用的是JDK动态代理的方式,如何切换CGlib?
针对基于注解的
<aop:aspectj-autoproxy proxy-target-class=“true”/>
针对传统的方式
<aop:config proxy-target-class="true">
<aop:pointcut id="myPointcut" expression="execution(* login(..))"/>
<!--组装额外功能和切入点-->
<aop:advisor advice-ref="around" pointcut-ref="myPointcut"/>
</aop:config>