簡(jiǎn)介
AOP:面向切面編程,是OOP的擴(kuò)展和延申祈纯,解決OOP中遇到的問題
可以進(jìn)行權(quán)限校驗(yàn)糠爬,日志記錄,性能監(jiān)控刽脖,事務(wù)校驗(yàn)
Spring底層的AOP實(shí)現(xiàn)原理:動(dòng)態(tài)代理
JDK動(dòng)態(tài)代理 :只能對(duì)實(shí)現(xiàn)了接口的類產(chǎn)生代理
Cglib動(dòng)態(tài)代理(類似于Javassist第三方代理技術(shù)):對(duì)沒有實(shí)現(xiàn)接口的類產(chǎn)生代理對(duì)象羞海,生成子
對(duì)象
兩種動(dòng)態(tài)代理
JDK動(dòng)態(tài)代理實(shí)例
1.建立一個(gè)接口:
public interface UserDao {
public void insert();
public void update();
}
2.接口實(shí)現(xiàn)類
public class UserDaoImpl implements UserDao {
@Override
public void insert() {
System.out.println("insert方法");
}
@Override
public void update() {
System.out.println("update方法");
}
}
3.編寫代理類JdkProxy
public class JdkProxy{
//將被增強(qiáng)的對(duì)象傳遞到代理當(dāng)中
private UserDao userDao;
public JdkProxy(UserDao userDao) {
this.userDao = userDao;
}
public UserDao creatProxy() {
UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(), this);
return userDaoProxy;
}
}
- 實(shí)現(xiàn)InvocationHandler接口,并重寫其中方法曲管,用于權(quán)限管理
public class JdkProxy implements InvocationHandler{
······
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("insert".equals(method.getName())) {
System.out.println("權(quán)限增強(qiáng)");
return method.invoke(userDao, args);
}
return method.invoke(userDao, args);
}
5.測(cè)試類
@Test
public void demo1() {
UserDao userDao=new UserDaoImpl();
//創(chuàng)建代理
UserDao proxy = new JdkProxy(userDao).creatProxy();
proxy.insert();
proxy.update();
}
Cglib動(dòng)態(tài)代理
第三方開源代碼生成類庫(kù)却邓,動(dòng)態(tài)添加類的屬性和方法
1.編寫接口和測(cè)試類(同上)
2.編寫代理類CglibProxy
public class CglibProxy {
private UserDaoImpl userDaoImpl;
public CglibProxy(UserDaoImpl userDaoImpl) {
this.userDaoImpl=userDaoImpl;
}
public UserDaoImpl createProxy() {
//1.創(chuàng)建Cglib的代理對(duì)象
Enhancer enhancer = new Enhancer();
//2.設(shè)置父類
enhancer.setSuperclass(userDaoImpl.getClass());
//3.設(shè)置回調(diào)
enhancer.setCallback(this);
//4.創(chuàng)建代理
UserDaoImpl proxy=(UserDaoImpl) enhancer.create();
return proxy;
}
3.CglibProxy 繼承MethodInterceptor類,并重寫其中方法
public class CglibProxy implements MethodInterceptor{
······
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 判斷是否是insert方法
if("insert".equals(method.getName())) {
//增強(qiáng)
System.out.println("權(quán)限校驗(yàn)");
return methodProxy.invokeSuper(proxy, args);
}
return methodProxy.invokeSuper(proxy, args);
}
}
4.測(cè)試類
@Test
public void demo1() {
UserDaoImpl userDao=new UserDaoImpl();
//創(chuàng)建代理
UserDaoImpl proxy = new CglibProxy(userDao).createProxy();
proxy.insert();
proxy.update();
}
Spring的AOP開發(fā)
簡(jiǎn)介
AOP思想最早是由AOP聯(lián)盟組織提出的院水,Spring是使用這種思想最好的框架
Spring的AOP有自己實(shí)現(xiàn)的方式(非常繁瑣)AspectJ是一個(gè)AOP框架腊徙,Spring引入了AspectJ作為自身開發(fā)
相關(guān)代碼編寫
1.新建切面類简十,并在切面類中MethodInterceptor類(環(huán)繞通知)
//環(huán)繞通知
public class MyAspectXML implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation arg0) throws Throwable {
System.out.println("0000000000");
Object obj=arg0.proceed();
System.out.println("0000000000");
return obj;
}
}
2.在xml文件中配置
<!-- 配置目標(biāo)對(duì)象,被增強(qiáng)的對(duì)象-->
<bean id="productDao" class="com.zut.aopTest.ProductDaoImpl"></bean>
<!-- 將切面類交給Spring管理 -->
<bean id="myAspect" class="com.zut.aopTest.MyAspectXML"></bean>
3.在xml文件中配置通知
<!-- 創(chuàng)建代理對(duì)象 默認(rèn)JDK動(dòng)態(tài)代理 如果沒有proxyInterfaces 則默認(rèn)cglib-->
<bean class="org.springframework.aop.framework.ProxyFactoryBean" name="proxyBean">
<property name="targetName" value="productDao"></property>
<!-- 攔截器名字 -->
<property name="interceptorNames" value="myAspect">
<!-- 如果通知在多個(gè)類中 -->
<!-- <array>
<value></value>
</array> -->
</property>
<property name="proxyInterfaces" value="com.zut.aopTest.ProductDao"></property>
</bean>
4.編寫測(cè)試類
public void demo1() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
ProductDao productDao=context.getBean("proxyBean",ProductDao.class);
productDao.save();
}
前置通知接口:MethodBrforeAdvice
后置通知接口:AfterReturningAdvice 注意: 有異常時(shí)后置通知不執(zhí)行
異常通知接口:ThrowsAdvice
最終通知接口:AfterAdvice
在此方式下撬腾,xml文件可以采用自動(dòng)織入編寫
1.xml文件
<!-- 配置目標(biāo)對(duì)象螟蝙,被增強(qiáng)的對(duì)象 -->
<bean id="productDao" class="com.zut.aopTest.ProductDaoImpl"></bean>
<!-- 將切面類交給Spring管理 -->
<bean id="myAspect" class="com.zut.aopTest.MyAspectXML"></bean>
<aop:config>
<aop:pointcut expression="execution(* com.zut.springAOP.UserImpl.*(..))" id="pointcut"/>
<aop:advisor advice-ref="myAspect" pointcut-ref="pointcut"/>
</aop:config>
//id 與 pointcut-ref 相等
2.測(cè)試類
public void demo1() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
ProductDao productDao=context.getBean("productDao",ProductDao.class);
productDao.save();
}
采用AspectJ的XML的方式
相關(guān)配置文件
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.5.3</version>
</dependency>
xml文件
相關(guān)代碼編寫
1.編寫接口和測(cè)試類
2.編寫切面類
public class MyAspectXML {
public void checkPri() {
System.out.println("權(quán)限校驗(yàn)。民傻。胰默。。漓踢。初坠。。彭雾。碟刺。");
}
}
3.在xml文件里進(jìn)行配置
<!-- 配置目標(biāo)對(duì)象,被增強(qiáng)的對(duì)象 -->
<bean id="productDao" class="com.aopTest.ProductDaoImpl"></bean>
<!-- 將切面類交給Spring管理 -->
<bean id="myAspect" class="com.aopTest.MyAspectXML"></bean>
<!-- 通過(guò)AOP的配置實(shí)現(xiàn)對(duì)目標(biāo)產(chǎn)生代理 -->
<aop:config>
<!-- 表達(dá)式配置那些類哪些方法需要進(jìn)行增強(qiáng) ()里面只寫參數(shù)類型 可以寫成 save(int ,String) and args(a,b) -->
<aop:pointcut expression="execution(* com.aopTest.ProductDaoImpl.save(..))" id="pointcut1"/>
<!-- 配置切面 myAspect指向切面 與上文要對(duì)應(yīng)上 checkPri 切面類里的方法名-->
<aop:aspect ref="myAspect">
<!--匹配參數(shù) 這里的名字要一致-->
<aop:before method="checkPri" pointcut-ref="pointcut1" arg-names="a"/></aop:aspect>
</aop:config>
</beans>
4.測(cè)試類
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:Spring-config.xml")
public class SpringDemo1 {
@Resource(name="productDao")
private ProductDao productDao;
@Test
public void demo1() {
productDao.save();
productDao.update();
productDao.find();
productDao.delete();
}
}
通知類型
前置通知
在目標(biāo)方法執(zhí)行之前執(zhí)行此操作
1.xml文件
<!-- 配置切面 -->
<aop:aspect ref="myAspect">
<aop:before method="checkPri" pointcut-ref="pointcut1"/>
</aop:aspect>
2.獲得切入點(diǎn)信息(幾種通知均可實(shí)現(xiàn))
在切入類中
public void checkPri(JoinPoint joinPoint) {
System.out.println("權(quán)限校驗(yàn)薯酝。半沽。。吴菠。者填。。做葵。占哟。。"+joinPoint);
}
后置通知
在目標(biāo)方法之后執(zhí)行的操作
1.在xml文件中
<aop:pointcut expression="execution(* com.aopTest.ProductDaoImpl.delete(..))" id="pointcut2"/>
<!-- 后置通知 -->
<aop:after-returning method="write" pointcut-ref="pointcut2" returning="result"/>
//returning表示獲取返回值
2.獲得方法返回值
在切入類中
/**
* 后置通知
*/
public void write(Object result) {
System.out.println("日志通知酿矢。榨乎。。瘫筐。蜜暑。。策肝。肛捍。。之众。"+result);
}
//這里的reult必須與前面配置里的returning一致
環(huán)繞通知
在目標(biāo)方法執(zhí)行之前和之后進(jìn)行操作
1.xml文件
<aop:pointcut expression="execution(* com.aopTest.ProductDaoImpl.update(..))" id="pointcut3"/>
<!-- 環(huán)繞通知 -->
<aop:around method="around" pointcut-ref="pointcut3"/>
2.在切入類
/**
* 環(huán)繞通知
* 性能監(jiān)控
* @throws Throwable
*/
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("環(huán)繞通知前拙毫。。棺禾。");
Object obj=joinPoint.proceed();
System.out.println("環(huán)繞通知后缀蹄。。。");
return obj;
}
異常拋出通知
在程序出現(xiàn)異常時(shí)袍患,進(jìn)行的操作
1.xml文件中
<aop:pointcut expression="execution(* com.aopTest.ProductDaoImpl.find(..))" id="pointcut4"/>
<!-- 異常通知 -->
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"/>
//throwing:得到異常類型
2.切入類
/**
* 異常拋出
*/
public void afterThrowing(Throwable ex) {
System.out.println("異常拋出坦康。。诡延。"+ex);
}
//ex必須與上文配置中的throwing一致
最終通知
無(wú)論代碼是否有異常滞欠,總是會(huì)執(zhí)行,相當(dāng)于finally代碼塊
1.xml文件里
<!-- 最終通知 -->
<aop:after method="after" pointcut-ref="pointcut4"/>
2.切面類
/**
* 最終通知
*/
public void after() {
System.out.println("最終通知肆良。筛璧。。...");
}
spring的切入點(diǎn)表達(dá)式寫法
基于execution的函數(shù)完成的
語(yǔ)法:
[訪問修飾符] 方法返回值 包名.類名.方法名(參數(shù))
public void com.zut.aopTest.ProductDao.save(..)
星號(hào)(*)代表任意惹恃,參數(shù)不能寫 * 夭谤,可以寫 ..
*****Dao.save(..)
*com.zut.aopTest.ProductDao+save(..)