代理模式及Spring AOP
代理模式:为其他对象提供一种代理以控制对这个对象的访问。
Subject:定义RealSubject和Proxy 的共用接口
RealSubject:定义Proxy所代理的实体
Proxy:保存一个指向真实实体的引用,并提供和真实实体相同的接口
Java实现:
package com.lbl.pojo;
public interface Subject {
void talk();
void run();
void jump();
void fly();
}
package com.lbl.pojo;
public class RealSubject implements Subject {
public void talk() {
System.out.println("talk");
}
public void run() {
System.out.println("run");
}
public void jump() {
System.out.println("jump");
}
public void fly() {
System.out.println("fly");
}
}
package com.lbl.pojo;
public class Proxy implements Subject{
private Subject subject;
public Proxy() {
this.subject = new RealSubject();
}
public void talk() {
subject.talk();
}
public void run() {
subject.run();
}
public void jump() {
subject.jump();
}
public void fly() {
subject.fly();
}
}
package com.lbl.pojo;
public class Client {
public static void main(String[] args) {
Proxy proxy = new Proxy();
proxy.run();
proxy.talk();
proxy.fly();
proxy.jump();
}
}
代理模式的应用
- 远程代理。为一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在于不同地址空间的事实
- 虚拟代理。根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象。如网页中一张张加载的图片
- 安全代理。用来控制真实对象的访问权限
- 智能指引。当调用真实的对象时,代理处理另外的一些事。
代理模式的分类
- 静态代理
- 动态代理
静态代理的缺点是每一个真实实体都会产生一个代理角色,开发效率变低。而动态代理可以解决这个问题。
动态代理
- 动态代理的代理类是动态生成的,不是事先写好的
- 动态代理分为两类:基于接口的动态代理,基于类的动态代理
- 基于接口:JDK动态代理
- 基于类:cglib
- Java字节码:javasist
基于接口要了解的两个类:Proxy,InvocationHandler
Proxy:提供了创建动态代理类和实例的静态方法
InvocationHandler:由代理实例调用处理程序实现的接口。
每个代理实例都有一个关联的调用处理程序,当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。
实例
我们有一个“战士(warrior)”接口,兽人战士(OrkWarrior)实现了“战士接口”,ProxyInvokeHandler类提供对兽人战士的代理访问,最后玩家(Player)通过代理操纵兽人战士。
package com.lbl.demo;
public interface Warrior {
void attack();
void defend();
void skill();
}
package com.lbl.demo;
public class OrkWarrior implements Warrior {
public void attack() {
System.out.println("为了部落!!");
}
public void defend() {
System.out.println("兽人永不为奴");
}
public void skill() {
System.out.println("大风车!");
}
}
package com.lbl.demo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvokeHandler implements InvocationHandler {
private Warrior warrior;
//设置需要代理的接口
public void setWarrior(Warrior warrior) {
this.warrior = warrior;
}
//获取代理
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(),warrior.getClass().getInterfaces(),this);
}
//执行接口中的方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(warrior,args);
}
}
package com.lbl.demo;
public class Player {
public static void main(String[] args) {
Warrior warrior = new OrkWarrior();
ProxyInvokeHandler pih = new ProxyInvokeHandler();
pih.setWarrior(warrior);
Warrior warrior1 = (Warrior) pih.getProxy();
warrior1.attack();
warrior1.defend();
warrior1.skill();
}
}
Spring中AOP的实现
Spring API实现
导入maven依赖:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
在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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
实例
有一个UserService接口,一个UserServiceImp的实现类;有两个打印日志的事务:BeforeLog(方法执行前打印),AfterLog(方法返回前打印)。现要将BeforeLog切入到UserService的add方法前,将AfterLog切入到UserService的delete方法后。
package com.lbl.service;
public interface UserService {
void add();
void delete();
void update();
void select();
}
package com.lbl.service;
public class UserServiceImp implements UserService {
public void add() {
System.out.println("add an user");
}
public void delete() {
System.out.println("delete an user");
}
public void update() {
System.out.println("update an user");
}
public void select() {
System.out.println("select an user");
}
}
package com.lbl.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class BeforeLog implements MethodBeforeAdvice {
//MethodBeforeAdvice接口用于切入方法执行前
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName() + "类的" + method.getName() + "方法被执行了");
}
}
package com.lbl.log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class AfterLog implements AfterReturningAdvice {
//AfterReturningAdvice用于切入方法返回前
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println(method.getName() + "被执行了,返回了" + o);
}
}
Spring配置文件:
<?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.lbl.service.UserServiceImp"/>
<bean id="beforeLog" class="com.lbl.log.BeforeLog"/>
<bean id="afterLog" class="com.lbl.log.AfterLog"/>
<bean id="myaop" class="com.lbl.myaop.DiyAop"/>
<aop:config>
<!-- 配置切入点-->
<aop:pointcut id="addpointcut" expression="execution(* com.lbl.service.UserServiceImp.add())"/>
<aop:pointcut id="deletepointcut" expression="execution(* com.lbl.service.UserServiceImp.delete())"/>
<!-- 配置执行环绕-->
<aop:advisor advice-ref="beforeLog" pointcut-ref="addpointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="deletepointcut"/>
</aop:config>
</beans>
Spring AOP 中pointcut expression表达式解析及配置_Java_敲码农的小代码 ;-CSDN博客
关于pointcut中的表达式(expression)详解
自定义切入
这种方法不使用Spring提供的接口,而在Spring配置文件里进行设置,不如前一种方法强大,因为前一种方法可以利用反射获取大量信息。
UserService接口和UserServiceImp实现类不变。新增一个自定义切面:
package com.lbl.myaop;
public class DiyAop {
public void beforeMethod() {
System.out.println("在方法执行之前");
}
public void afterMethod() {
System.out.println("在方法执行之后");
}
}
修改配置文件:
<?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.lbl.service.UserServiceImp"/>
<bean id="beforeLog" class="com.lbl.log.BeforeLog"/>
<bean id="afterLog" class="com.lbl.log.AfterLog"/>
<bean id="myaop" class="com.lbl.myaop.DiyAop"/>
<aop:config>
<!-- 注册一个切面-->
<aop:aspect ref="myaop">
<!-- 注册一个切入点-->
<aop:pointcut id="diypc" expression="execution(* com.lbl.service.UserServiceImp.*(..))"/>
<!-- 选择切入方法及位置和切入点-->
<aop:before method="beforeMethod" pointcut-ref="diypc"/>
<aop:after method="afterMethod" pointcut-ref="diypc"/>
</aop:aspect>
</aop:config>
</beans>
使用注解实现AOP
package com.lbl.myaop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect //声明这是一个切面
public class Annopc {
//声明切入位置和切入点
@Before("execution(* com.lbl.service.UserServiceImp.add())")
public void beforeMethod() {
System.out.println("注解方式,方法执行前");
}
}
在配置文件中注册bean并引入对AOP注解的支持:
<bean id="annopc" class="com.lbl.myaop.Annopc"/>
<!-- proxy-target-class为false是基于JDK,为true是基于cglib,但两者执行的结果相同-->
<aop:aspectj-autoproxy proxy-target-class="false"/>