10、代理模式
为什么要学习代理模式?因为这就是SpringAOP的底层!【SpringAOP 和 SpringMVC】
代理模式的分类:
- 静态代理
- 动态代理
10.1、静态代理
角色分析:
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后,一般会做一些附属操作
- 客户:访问代理对象的人
代码步骤:
-
接口
//租房 public interface Rent { public void rent(); }
-
真实角色
//房东 public class Host implements Rent{ public void rent() { System.out.println("房东要出租房子"); } }
-
代理角色
//房屋中介 public class Proxy implements Rent{ private Host host; public Proxy() { } public Proxy(Host host) { this.host = host; } public void rent() { seeHouse(); host.rent(); heTong(); fare(); } //看房 public void seeHouse(){ System.out.println("中介带你看房"); } //中介费 public void fare(){ System.out.println("收中介费"); } //签合同 public void heTong(){ System.out.println("签租赁合同"); } }
-
客户端访问代理角色java
public class Client { public static void main(String[] args) { //房东要出租房屋 Host host=new Host(); //代理,中介帮房东出租房租,但是代理角色会有附属操作 Proxy proxy = new Proxy(host); //租房子不用面对房东,直接找中介租房 proxy.rent(); } }
代理模式的好处:
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
- 公共业务也就交给代理角色!实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!
缺点:
- 一个真实角色就会产生一个代理角色;代码量会翻倍 - 开发效率会变低。
10.2、加深理解
需求:service输出加上log注解
代码
service接口
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
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("查询了一个用户");
}
//注意:在公司中一般,不改动原有的业务代码!
}
使用代理类实现log
public class UserServiceProxy implements UserService {
private UserServiceImpl userService;
public UserServiceProxy() {
}
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("[Debug]使用了"+msg+"方法");
}
}
//输出
[Debug]使用了delete方法
删除了一个用户
聊聊AOP
10.3、动态代理
-
动态代理和静态代理角色一样
-
动态代理的代理类是动态生成的,不是我们直接写好的!
-
动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口—JDK 动态代理 【默认】
- 基于类:cglib
- Java字节码实现:javasist
需要了解两个类:Proxy:代理,InvocationHandler:调用处理程序
Proxy:创建动态代理类的实例提供了静态方法,也是所有动态代理类的父类的方法创建
InvocationHandler是通过一个代理实例零调用处理程序实现的接口。
每个代理实例都有一个相关的调用处理程序。当一个方法是在一个代理实例调用,调用的方法进行编码并派遣其调用处理程序的invoke方法。
动态代理的好处:
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
- 公共业务也就交给代理角色!实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!
- 一个动态代理类代理的是一个接口,一般就是对应一类业务
- 一个动态代理类可以代理多个类,只是实现了同一个接即可!
10.4、动态代理模板
//代理工具类
//使用这个类 自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成得到的代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
//处理代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
//动态代理的本质,就是使用反射机制实现
Object result = method.invoke(target, args);
return result;
}
public void log(String msg){
System.out.println("[debug]执行了"+msg+"方法");
}
}
使用
public class Client {
public static void main(String[] args) {
//真实角色
UserServiceImpl userService=new UserServiceImpl();
//代理角色,不存在
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setTarget(userService);//设置要代理的对象
//动态生成代理类
UserService proxy = (UserService) pih.getProxy();
proxy.delete();
}
}
11、AOP
11.1、什么是AOP
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
11.2、AOP在Spring中的作用
提供声明式事务;允许用户自定义切面
- 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点,如日志、安全、缓存、事务等等…
- 切面(ASPECT): 横切关注点 被模块化 的特殊对象,即,它是类中的一个方法。
- 通知(Advice):切面必须要完成的工作,即,它是类中的一个方法。
- 目标(Target):被通知的对象。
- 代理(Proxy):向目标对象应用通知后创建的对象。
- 切入点(PointCut):切面通知 执行的“地点”的定义。
- 连接点(JointPoint):与切入点匹配的执行点。
SpringAOP中通过Advice定义横切逻辑,Spring中支持5中类型的Advice
即在AOP在不改变原有代码的情况下,去增加新的功能。
11.3、使用Spring实现AOP
【重点】使用AOP织入,需要导入一个依赖包!
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd"
>
<bean id="userService" class="com.chif.service.UserServiceImpl"/>
<bean id="log" class="com.chif.log.Log"/>
<bean id="afterLog" class="com.chif.log.AfterLog"/>
<!--方式一:使用原生Spring API接口-->
配置aop:需要导入aop的约束
<aop:config>
<!--切入点; expression :表达式,execution(要执行的位置! **** )-->
<aop:pointcut id="pointcut" expression="execution(* com.chif.service.UserServiceImpl.*(..))"/>
<!--执行环绕增加-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
<!--方式二:自定义类-->
<bean id="diy" class="com.chif.diy.DiyPointCut"/>
<aop:config>
<!--自定义切面,ref 要引用的类-->
<aop:aspect ref="diy">
<!--切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.chif.service.UserServiceImpl.*(..))"/>
<!--通知-->
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
<!--方式三-->
<bean id="annotationPointCut" class="com.chif.diy.AnnotationPointCut"/>
<!--开启注解支持-->
<aop:aspectj-autoproxy/>
</beans>
方式一:使用Spring的API接口【主要是SpringAPI接口实现】
//前置通知
public class Log implements MethodBeforeAdvice {
//method:要执行的目标对象的方法
//objects:参数
//o : target
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName()+"的"+method.getName()+"被执行了");
}
}
//返回值后置通知
public class AfterLog implements AfterReturningAdvice {
/*
参数l:返回值
*/
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("执行了"+method.getName()+"返回值为"+o);
}
}
方式二:使用自定义类来实现AOP【主要是切面定义】
public class DiyPointCut {
public void before(){
System.out.println("====方法执行前=====");
}
public void after(){
System.out.println("====方法执行后=====");
}
}
方式三:使用注解实现
//方式三,使用注解的方式实现AOP
@Aspect//标注这个类是一个切面
public class AnnotationPointCut {
@Before("execution(* com.chif.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("anno-方法执行前");
}
@After("execution(* com.chif.service.UserServiceImpl.*(..))")
public void After(){
System.out.println("anno-方法执行后");
}
//在环绕增强中,我们可以给定一个参数,代表我们要处理切入的点
@Around("execution(* com.chif.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
//执行方法
Object proceed = jp.proceed();
System.out.println("环绕后");
}
}
测试
public class Mytest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理代理的是接口
UserService userService = context.getBean("userService", UserService.class);
userService.query();
}
}
"C:\Program Files\Java\jdk1.8.0_221\bin\java.exe" "-javaagent:D:\IDEAIU\IntelliJ IDEA 2018.3.2\lib\idea_rt.jar=59181:D:\IDEAIU\IntelliJ IDEA 2018.3.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_221\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\rt.jar;D:\IDEAIU\spring-01\spring-07-AOP\target\test-classes;D:\IDEAIU\spring-01\spring-07-AOP\target\classes;D:\maven_local_reponsitory\repository\org\aspectj\aspectjweaver\1.9.4\aspectjweaver-1.9.4.jar;D:\maven_local_reponsitory\repository\org\springframework\spring-webmvc\5.2.0.RELEASE\spring-webmvc-5.2.0.RELEASE.jar;D:\maven_local_reponsitory\repository\org\springframework\spring-aop\5.2.0.RELEASE\spring-aop-5.2.0.RELEASE.jar;D:\maven_local_reponsitory\repository\org\springframework\spring-beans\5.2.0.RELEASE\spring-beans-5.2.0.RELEASE.jar;D:\maven_local_reponsitory\repository\org\springframework\spring-context\5.2.0.RELEASE\spring-context-5.2.0.RELEASE.jar;D:\maven_local_reponsitory\repository\org\springframework\spring-core\5.2.0.RELEASE\spring-core-5.2.0.RELEASE.jar;D:\maven_local_reponsitory\repository\org\springframework\spring-jcl\5.2.0.RELEASE\spring-jcl-5.2.0.RELEASE.jar;D:\maven_local_reponsitory\repository\org\springframework\spring-expression\5.2.0.RELEASE\spring-expression-5.2.0.RELEASE.jar;D:\maven_local_reponsitory\repository\org\springframework\spring-web\5.2.0.RELEASE\spring-web-5.2.0.RELEASE.jar;D:\maven_local_reponsitory\repository\junit\junit\4.12\junit-4.12.jar;D:\maven_local_reponsitory\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" Mytest
com.chif.service.UserServiceImpl的query被执行了
====方法执行前=====
环绕前
anno-方法执行前
查询加了一个用户
环绕后
anno-方法执行后
====方法执行后=====
执行了query返回值为null
Process finished with exit code 0