1.AOP优点
AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码
2.AOP底层原理(简介)
aop底层原理就是动态代理技术 。动态代理技术又分为jdk动态代理和cglib动态代理,什么叫代理?代理就相当于现实生活中的中介或经纪人。
一、 jdk动态代理
jdk动态代理技术必须目标类实现了某个接口,否则无法使用jdk动态代理技术, jdk动态代理底层是使用的字节拼码接技术(ASM框架),本质是动态生成了,一个UserDao接口的实现类 $Proxy0类
首先编写一个接口
public interface UserDao {
int add(int a, int b);
}
然后再编写实现类实现接口
public class UserDaoImpl implements UserDao{
@Override
public int add(int a, int b) {
return a+b;
}
}
这个时候编写一个测试类所得的结果是正确的,然后我们再来编写一个“黑客类” 实现InvocationHandler接口
public class JdkHk implements InvocationHandler{
Object target;//目标
public JdkHk(Object target) {
this.target = target;
}
/**
* 入侵目标的方法
* proxy:代理对象
* method: 目标对象的方法
* args:调用目标对象方法传递过参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("====亲,你被入侵了===");
//改变传入的参数
args[0]=100;
args[1]=2;
//调用目标方法
System.out.println("======鉴权成功=====");
Object result = method.invoke(target, args);
System.out.println("result=="+result);
System.out.println("======记录成功======");
return result;
}
}
最后我们再来测试
public class TestOne {
public static void main(String[] args) {
// 一、正常调用
UserDao userDao = new UserDaoImpl();
int result = userDao.add(3, 5);
System.out.println(result);
// 二、jdkHk入侵调用
// 1.目标对象
UserDao target = new UserDaoImpl();
// 2.黑客对象
JdkHk jdkHk = new JdkHk(target);
// 3.生产代理对象
UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
new Class[] {
UserDao.class }, jdkHk);
result = userDaoProxy.add(3, 50);
}
}
运行结果
总结:JDK动态代理只能代理实现接口的目标对象
二、cglib动态代理
创建一个用来入侵的“黑客”类
public class SpringProxy implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("spring代理前");
//执行方法
Object obj = invocation.proceed();
System.out.println("spring代理后");
return obj;
}
}
配置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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 1、目标对象 -->
<bean id="target2" class="com.cc.dao.impl.UserDaoImpl"></bean>
<!-- 2、黑客对象 -->
<bean id="jdkHk" class="com.cc.proxy.JdkHk">
<constructor-arg value=""/>
</bean>
<!--3、代理对象:是目标对象与黑客对象融合体(jdk) -->
<bean id="userDaoProxyJdk" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="com.cc.dao.UserDao"></property>
<!-- 1)注入目标对象 -->
<property name="target" ref="target2"/>
<!-- 2)黑客对象 -->
<property name="interceptorNames">
<array>
<value>jdkHk</value>
</array>
</property>
</bean>
</beans>
最后编写测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-beans.xml")
public class SpringHkTest {
@Autowired//默认按类型注入,通过@Qualifier修改为按名称注入
@Qualifier("userDaoProxy")
private UserDaoImpl2 userDao2;//cglib
@Autowired
@Qualifier("userDaoProxy2")
//@Resource(name="userDaoProxy2")//默认按类型注入,通过@Qualifier修改为按名称注入
private UserDao userDao;//jdk
@Test
public void test01() {
//目标没有实现接口--cglib
System.out.println(userDao2.add(3, 5));
}
}
总结:
Spring框架,如果类实现了接口,就使用JDK的动态代理生成代理对象,如果这个类没有实现任何接口,使用CGLIB生成代理对象
当然也可以通过 ProxyFactoryBean的ProxyTargetClass属性设置为true,强制使用cglib动态代理
Cglib动态代理是为目标类生成代理类(子类)、JDK动态代理是为目标接口生成代理类(子类),而非目标类的代理类。
Cglib既可以代理没有接口的目标对象,也可以代理实现接口的目标对象!!!