代理机制
Spring 的AOP 部分使用使用JDK动态代理,部分使用CGLIB来为目标对象创建代理。如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理;如果目标对象没有实现任何接口,则会创建CGLIB动态代理。CGLIB是第三方包,从Spring4.3开始就无需再次导入包了。
注意
1)客户端直接调用与目标类相对应的代理类的功能,
2)为了保证代理类与目标类具有相同功能,代理类与目标类必须实现相同的接口
代理是实现AOP的核心和关键技术。只要是AOP,就一定会涉及代理技术
. 代理技术的分类
按照是否是在程序运行期间产生代理类可以将代理分为静态代理和动态代理
1.静态代理:就是手动为每一个目标类的每一个方法都增加交叉业务,也就是手动为每一个目标类增加代理类
缺点:如果目标类数量非常多或者目标类中的功能非常多,直接使用静态代理的方式来为目标类增加交叉业务会非常的繁琐。
2.动态代理:通过特定的设置,在程序运行期间指示JVM动态地生成类的字节码。这种动态生成的类往往被用作代理类,即动态代理类。也就是运行时做 编译的事情并且把生成的字节码加载成这个类的Class对象
首先是静态代理
创建一个抽象角色,比如咋们平时做的用户业务,抽象起来就是增删改查!
编写接口方法
package com.ice.spring.service;
//抽象角色:增删改查业务
public interface UserService {
void add();
void delete();
void update();
void query();
}
我们需要一个真实对象来完成这些增删改查操作,也就是接口的实现类
package com.ice.spring.service;
//真实对象,完成增删改查操作的人
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("增加了一个用户");
}
public void delete() {
System.out.println("删除了一个用户");
}
public void update() {
System.out.println("更新了一个用户");
}
public void query() {
System.out.println("查询了一个用户");
}
}
现在我们需要增加一个日志功能,怎么实现!
思路1 :在实现类上增加代码 繁琐,影响业务
思路2:使用静态代理来做,能够不改变原来的业务情况下,可以实现此功能
设置一个代理类来处理日志! 代理角色
package com.ice.spring.service;
//代理角色,在这里面增加日志的实现
public class UserServiceProxy implements UserService {
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
public void add() {
log("add");
userService.add();
}
public void delete() {
log("delete");
userService.delete();
}
public void update() {
log("update");
userService.update();
}
public void query() {
log("query");
userService.query();
}
public void log(String msg){
System.out.println("执行了"+msg+"方法");
}
}
测试访问类:
package com.ice.spring;
import com.ice.spring.service.UserServiceImpl;
import com.ice.spring.service.UserServiceProxy;
public class StaticTest {
public static void main(String[] args) {
//真实业务
UserServiceImpl userService = new UserServiceImpl();
//代理类
UserServiceProxy proxy = new UserServiceProxy();
//使用代理类实现日志功能!
proxy.setUserService(userService);
proxy.add();
}
}
运行结果
总节
缺点:
1)目标类增多,代理类会成倍增多
2)接口和功能增加了修改了,会影响较多的实现类,厂家类,代理类都需要修改,工作量巨大
动态代理
1.动态代理的角色和静态代理的一样 .
2.动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的
3.动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
1) 基于接口的动态代理——JDK动态代理
2) 基于类的动态代理—cglib动态代理
JDK的动态代理
核心 : InvocationHandler 接口和 Proxy类
1.前提:要求目标类必须实现某些接口
2.JVM生成的动态代理类只能作用于具有相同接口目标类的代理类
3.要求目标类实现特定的接口才能生成和目标类对应的动态代理类
代码的实现
接口
package com.ice.spring.service;
public interface booksService {
public void add();
public void delete();
public void update();
public void select();
}
接口的实现类
//真实对象,完成增删改查操作的人
package com.ice.spring.service;
import org.mybatis.spring.support.SqlSessionDaoSupport;
public class booksServiceImpl implements booksService{
@Override
public void add() {
System.out.println("添加书籍");
}
@Override
public void update() {
System.out.println("更新书籍");
}
@Override
public void delete() {
System.out.println("删除书籍");
}
@Override
public void select() {
System.out.println("查询书籍");
}
}
现在我们需要增加新的功能,写我们的增强类 , 我们编写两个 , 一个前置增强 一个后置增强
前置增强
package com.ice.spring.Log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class beforeLog implements MethodBeforeAdvice {
//Method 要执行的目标对象的方法
//args 参数
//target 目标对象
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("开始日志");
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
}
}
后置增强
package com.ice.spring.Log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class afterLog implements AfterReturningAdvice {
@Override
//返回值 returnValue
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"方法被执行了"+"结果返回为"+returnValue);
System.out.println("结束日志");
}
}
最后去spring的文件中注册 , 并实现aop切入实现 , 注意导入约束 .
<?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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--
xmlns 即 xml namespace xml 使用的命名空间
xmlns:xsi 即 xml schema instance xml 遵守的具体规范
xsi:schemaLocation 本文档 xml 遵守的规范 官方指定
-->
<!--注册bean-->
<!--使用spring APi 接口-->
<bean id="booksService" class="com.ice.spring.service.booksServiceImpl" />
<bean id="beforeLog" class="com.ice.spring.Log.beforeLog"/>
<bean id="afterLog" class="com.ice.spring.Log.afterLog"/>
<!--配置AOP 需要配置AOP的约束-->
<aop:config>
<!--切入点 -->
<aop:pointcut id="pointcut" expression="execution (* com.ice.spring.service.booksServiceImpl.*())"/>
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
<!-- <aop:aspectj-autoproxy proxy-target-class="true"/>-->
</beans>
动态代理测试
package com.ice.spring;
import com.ice.spring.service.booksService;
import com.ice.spring.service.booksServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
//方式一 使用spring的api接口实现aop
public class AOPtest {
public static void main(String[] args) {
//创建容器
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理代理的是接口
booksService booksService=(booksService) context.getBean("booksService");
System.out.println(booksService.getClass().getName());
booksService.select();
}
}
运行结果(执行JDK动态代理 com.sun.proxy.$Proxy5)
CGLIB的动态代理
1.目标类没有实现相应的接口,又需要为这个类动态生成代理类。此时第三方类库CGLIB是最好的选择。
2.CGLIB可以为目标类动态生成目标类的子类,并把这个动态生成的子类作为这个类的代理类。
代码实现一(有接口的情况)
只需要将实现JDK动态代理的代码稍作修改即可
1.在spring配置文件添加如下代码
<aop:aspectj-autoproxy proxy-target-class="true"/>
运行结果(执行了CGLIB动态代理(com.ice.spring.service.booksServiceImpl E n h a n c e r B y S p r i n g C G L I B EnhancerBySpringCGLIB EnhancerBySpringCGLIBb439268e)
代码实现二(没有接口的情况)
1.编写没有实现任何接口的目标类
package com.ice.spring.service;
//真实对象,完成增删改查操作的人
public class UserServiceCglib {
public void add() {
System.out.println("增加了一个用户");
}
public void delete() {
System.out.println("删除了一个用户");
}
public void update() {
System.out.println("更新了一个用户");
}
public void query() {
System.out.println("查询了一个用户");
}
}
2.在spring配置文件中注册bean
<?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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--
xmlns 即 xml namespace xml 使用的命名空间
xmlns:xsi 即 xml schema instance xml 遵守的具体规范
xsi:schemaLocation 本文档 xml 遵守的规范 官方指定
-->
<!--注册bean-->
<!--使用spring APi 接口-->
<bean id="booksService" class="com.ice.spring.service.booksServiceCglib" />
<bean id="beforeLog" class="com.ice.spring.Log.beforeLog"/>
<bean id="afterLog" class="com.ice.spring.Log.afterLog"/>
<!--配置AOP 需要配置AOP的约束-->
<aop:config>
<!--切入点 -->
<aop:pointcut id="pointcut" expression="execution (* com.ice.spring.service.booksServiceCglib.*())"/>
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
3.注意:无需写代理类,由spring自动生成
4.编写测试类代码
package com.ice.spring;
import com.ice.spring.service.booksService;
import com.ice.spring.service.booksServiceCglib;
import com.ice.spring.service.booksServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
//方式一 使用spring的api接口实现aop
public class AOPtest {
public static void main(String[] args) {
//创建容器
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理代理的是接口
booksServiceCglib booksServiceCglib=(booksServiceCglib) context.getBean("booksService");
System.out.println(booksServiceCglib.getClass().getName());
booksServiceCglib.select();
}
}
运行结果(在没有实现接口的情况下,spring默认开启了CGLIB,无需手动开启)
总结:
一、有接口的情况
1)默认使用JDK动态代理
2)可以开启CGLIB动态代理,该方式执行效率远远高于JDK的
开启方法如下
<aop:aspectj-autoproxy proxy-target-class="true"/>
二、没有接口的情况
1)默认开启CGLIB动态代理,由spring自动生成代理类,该方式执行效率远远高于JDK的