代理模式及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"/>