第五章、AOP底层实现原理
1. 核心问题
1.AOP如何创建动态代理类(动态字节码技术)
2.Spring工厂如何加工创建代理对象
通过原始对象的id,获得的是代理对象
2. 动态代理的创建
2.1JDK的动态代理 (原理 + 编码)
1.Proxy.newProxyInstance方法参数详解
2.编码
public class TestJDKProxy {
/*
1.借类加载器 TestJDKProxy 或 UserServiceImpl 都可以
2.JDK8.x 前必须加 final
*/
public static void main(String[] args) {
//1.创建原始对象
UserServiceImpl userService = new UserServiceImpl();
//2.JDK创建动态代理
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("---------proxy log----------");
//原始方法运行
Object ret = method.invoke(userService, args);
return ret;
}
};
UserService userServiceProxy =(UserService) Proxy.newProxyInstance(TestJDKProxy.class.getClassLoader(), userService.getClass().getInterfaces(), handler);
userServiceProxy.register(new User());
userServiceProxy.login("liulei","123456");
}
}
2.2 CGlib的动态代理
CGlib创建动态代理的原理:父子继承关系创建对象,父类为原始类,子类为代理类,这样既可以保证二者方法一致,同时能够在代理类中提供新的实现(额外功能+原始方法)
CGlib编码
public class TestCGlib {
public static void main(String[] args) {
//1.创建原始对象
UserService userService = new UserService();
//2.通过CGlib的方式创建动态代理对象
//对比JDK:Proxy.newProxyInstance(classloader,interfaces,invocationhandler)
/*
Enhancer.setClassLoader()
Enhancer.setSuperClass() 等同interface
Enhancer.setCallBack() ---> MethodInterceptor(cglib) 接口类型
Enhancer.createProxy() ---> 创建代理对象
*/
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(TestCGlib.class.getClassLoader());
enhancer.setSuperclass(userService.getClass());
MethodInterceptor interceptor = new MethodInterceptor() {
//等同InvocationHandler----invoke
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("-----log------");
Object ret = method.invoke(userService, args);
return ret;
}
};
enhancer.setCallback(interceptor);
UserService userServiceProxy = (UserService)enhancer.create();
userServiceProxy.login("liulei","13245");
userServiceProxy.register(new User());
}
}
总结
1.JDK动态代理 Proxy。newProxyInstance() 通过接口创建代理的实现类
2.Cglib动态代理 Enhancer 通过继承父类创建代理的实现类
3.Spring工厂如何加工原始对象
思路分析
编码
public class ProxyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
/*
Proxy.newProxyInstance();
*/
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("---------log--------");
Object ret= method.invoke(bean, args);
return ret;
}
};
return Proxy.newProxyInstance(ProxyBeanPostProcessor.class.getClassLoader(),bean.getClass().getInterfaces(),handler);
}
}
配置文件
<bean id="userService" class="com.liulei.factory.UserServiceImpl"/>
<!-- 1.实现BeanPostProcessor进行加工-->
<!-- 2.配置文件中对BeanPostProcessor进行配置-->
<bean id="proxyBeanPostProcessor" class="com.liulei.factory.ProxyBeanPostProcessor"/>
第六章、基于注解的AOP编程
1.基于注解的AOP编程的开发步骤
1.原始对象
public interface UserService {
void register(User user);
boolean login(String name,String password);
}
public class UserServiceImpl implements UserService {
@Override
public void register(User user) {
System.out.println("UserServiceImpl.register 业务运算 + DAO");
// throw new RuntimeException("测试异常");
}@Override
public boolean login(String name, String password) {
System.out.println("UserServiceImpl.login");
return true;
}
}
2.额外功能
3.切入点
4.组装切面
#通过切面类定义了 额外功能 @Around
切入点 @Around(* login(..))
@Aspect 切面类
2.3.4都放在MyAspect类中完成
@Aspect
/*
1. 额外功能
public class MyAround implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) {
Object ret = invocation.invoke();
return ret;
}
}
<bean id="around" class="com.liulei.dynamic.Around"/>2. 切入点
<aop:config>
<aop:pointcut id="pc" expression="execution(* login(..)))"/>
<aop:advisor advice-ref="around" pointcut-ref="pc"/>
</aop:config>
*/
public class MyAspect {
@Around("execution(* login(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("-------------log-----------");
Object ret = joinPoint.proceed();
return ret;
}
}
配置文件
<bean id="userService" class="com.liulei.aspect.UserServiceImpl"/>
<!-- 切面-->
<!-- 1.额外功能-->
<!-- 2.切入点-->
<!-- 3.组装切面-->
<bean id="around" class="com.liulei.aspect.MyAspect"/><!-- 告知Spring基于注解来进行aop编程-->
<aop:aspectj-autoproxy/>
</beans>
2.细节
切入点复用:在切面类中定义一个函数,上面@Pointcut注解 通过这样的方式,定义切入点表达式,有利于切入点的复用
@Aspect
public class MyAspect {
@Pointcut("execution(* login(..))")
public void myPointcut(){}
@Around(value = "myPointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("-------------log-----------");
Object ret = joinPoint.proceed();
return ret;
}@Around(value = "myPointcut()")
public Object around1(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("-------------logty-----------");
Object ret = joinPoint.proceed();
return ret;
}
}
2.动态代理对象的创建
AOP底层实现 2种代理创建方式
1.JDK 通过实现接口 做新的实现类方式 创建代理对象
2.Cglib 通过继承父类 做新的子类 创建代理对象
默认:AOP编程 底层应用JDK动态代理创建方式
如果切换Cglib
1.基于注解AOP开发
<aop:aspectj-autoproxy proxy-target-class="true"/>
2.传统的AOP开发
<aop:config proxy-target-class="true">
...
</aop:config>
第七章、AOP开发中的一个坑
坑:在同一个业务类中,进行业务方法间的相互调用,只有最外层的方法,才是加入额外功能(内部的方法,通过普通的方式调用,调用的都是原始方法),如果想让内层的方法也调用代理对象的方法的,ApplicationContextAware获得工厂,进而获得代理对象
public class UserServiceImpl implements UserService, ApplicationContextAware {
private ApplicationContext ctx;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ctx = applicationContext;
}
@Log
@Override
public void register(User user) {
System.out.println("UserServiceImpl.register 业务运算 + DAO");
// throw new RuntimeException("测试异常");
//this.login("liulei","123465");
/*
this.login("liulei","123465"); 这么写调用的是本类的 login 方法, 即原始对象的 login 方法
为什么不在这里创建一个工厂获取代理对象呢?
Spring的工厂是重量级资源, 一个应用中应该只创建一个工厂.
因此我们必须通过 ApplicationContextAware 拿到已经创建好的工厂
*/
UserService userService = (UserService)ctx.getBean("userService");
userService.login("liulei","123456");
}
@Override
public boolean login(String name, String password) {
System.out.println("UserServiceImpl.login");
return true;
}