到目前爲止,已經簡單學習了Spring的Core模塊、....因而咱們就開啓了Spring的AOP模塊了...在講解AOP模塊以前,首先咱們來說解一下cglib代理、以及怎麼手動實現AOP編程php
在講解cglib以前,首先咱們來回顧一下靜態代理和動態代理....我以前就寫過了靜態代理、動態代理的博文:blog.csdn.net/hon_3y/arti…java
因爲靜態代理須要實現目標對象的相同接口,那麼可能會致使代理類會很是很是多....很差維護---->所以出現了動態代理spring
動態代理也有個約束:目標對象必定是要有接口的,沒有接口就不能實現動態代理.....----->所以出現了cglib代理express
cglib代理也叫子類代理,從內存中構建出一個子類來擴展目標對象的功能!編程
接下來咱們就講講怎麼寫cglib代理:微信
//須要實現MethodInterceptor接口
public class ProxyFactory implements MethodInterceptor{
// 維護目標對象
private Object target;
public ProxyFactory(Object target){
this.target = target;
}
// 給目標對象建立代理對象
public Object getProxyInstance(){
//1. 工具類
Enhancer en = new Enhancer();
//2. 設置父類
en.setSuperclass(target.getClass());
//3. 設置回調函數
en.setCallback(this);
//4. 建立子類(代理對象)
return en.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("開始事務.....");
// 執行目標對象的方法
//Object returnValue = method.invoke(target, args);
proxy.invokeSuper(object, args);
System.out.println("提交事務.....");
return returnValue;
}
}
複製代碼
public class App {
public static void main(String[] args) {
UserDao userDao = new UserDao();
UserDao factory = (UserDao) new ProxyFactory(userDao).getProxyInstance();
factory.save();
}
}
複製代碼
使用cglib就是爲了彌補動態代理的不足【動態代理的目標對象必定要實現接口】session
AOP 面向切面的編程:app
下面咱們來看一段代碼:框架
// 保存一個用戶
public void add(User user) {
Session session = null;
Transaction trans = null;
try {
session = HibernateSessionFactoryUtils.getSession(); // 【關注點代碼】
trans = session.beginTransaction(); // 【關注點代碼】
session.save(user); // 核心業務代碼
trans.commit(); //…【關注點代碼】
} catch (Exception e) {
e.printStackTrace();
if(trans != null){
trans.rollback(); //..【關注點代碼】
}
} finally{
HibernateSessionFactoryUtils.closeSession(session); ////..【關注點代碼】
}
}
複製代碼
public interface IUser {
void save();
}
複製代碼
咱們一步一步來分析,首先咱們的UserDao有一個save()方法,每次都要開啓事務和關閉事務ide
//@Component -->任何地方都能用這個
@Repository //-->這個在Dao層中使用
public class UserDao {
public void save() {
System.out.println("開始事務");
System.out.println("DB:保存用戶");
System.out.println("關閉事務");
}
}
複製代碼
//@Component -->任何地方都能用這個
@Repository //-->這個在Dao層中使用
public class UserDao {
public void save() {
begin();
System.out.println("DB:保存用戶");
close();
}
public void begin() {
System.out.println("開始事務");
}
public void close() {
System.out.println("關閉事務");
}
}
複製代碼
public class AOP {
public void begin() {
System.out.println("開始事務");
}
public void close() {
System.out.println("關閉事務");
}
}
複製代碼
@Repository //-->這個在Dao層中使用
public class UserDao {
AOP aop;
public void save() {
aop.begin();
System.out.println("DB:保存用戶");
aop.close();
}
}
複製代碼
public class ProxyFactory {
//維護目標對象
private static Object target;
//維護關鍵點代碼的類
private static AOP aop;
public static Object getProxyInstance(Object target_, AOP aop_) {
//目標對象和關鍵點代碼的類都是經過外界傳遞進來
target = target_;
aop = aop_;
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
aop.begin();
Object returnValue = method.invoke(target, args);
aop.close();
return returnValue;
}
}
);
}
}
複製代碼
//把該對象加入到容器中
@Component
public class AOP {
public void begin() {
System.out.println("開始事務");
}
public void close() {
System.out.println("關閉事務");
}
}
複製代碼
@Component
public class UserDao {
public void save() {
System.out.println("DB:保存用戶");
}
}
複製代碼
<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="proxy" class="aa.ProxyFactory" factory-method="getProxyInstance">
<constructor-arg index="0" ref="userDao"/>
<constructor-arg index="1" ref="AOP"/>
</bean>
<context:component-scan base-package="aa"/>
</beans>
複製代碼
測試,獲得UserDao對象,調用方法
public class App {
public static void main(String[] args) {
ApplicationContext ac =
new ClassPathXmlApplicationContext("aa/applicationContext.xml");
IUser iUser = (IUser) ac.getBean("proxy");
iUser.save();
}
}
複製代碼
上面使用的是工廠靜態方法來建立代理類對象。咱們也使用一下非靜態的工廠方法建立對象。
package aa;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/** * Created by ozc on 2017/5/11. */
public class ProxyFactory {
public Object getProxyInstance(final Object target_, final AOP aop_) {
//目標對象和關鍵點代碼的類都是經過外界傳遞進來
return Proxy.newProxyInstance(
target_.getClass().getClassLoader(),
target_.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
aop_.begin();
Object returnValue = method.invoke(target_, args);
aop_.close();
return returnValue;
}
}
);
}
}
複製代碼
配置文件:先建立工廠,再建立代理類對象
<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--建立工廠-->
<bean id="factory" class="aa.ProxyFactory"/>
<!--經過工廠建立代理-->
<bean id="IUser" class="aa.IUser" factory-bean="factory" factory-method="getProxyInstance">
<constructor-arg index="0" ref="userDao"/>
<constructor-arg index="1" ref="AOP"/>
</bean>
<context:component-scan base-package="aa"/>
</beans>
複製代碼
Aop: aspect object programming 面向切面編程
關注點:
// 保存一個用戶
public void add(User user) {
Session session = null;
Transaction trans = null;
try {
session = HibernateSessionFactoryUtils.getSession(); // 【關注點代碼】
trans = session.beginTransaction(); // 【關注點代碼】
session.save(user); // 核心業務代碼
trans.commit(); //…【關注點代碼】
} catch (Exception e) {
e.printStackTrace();
if(trans != null){
trans.rollback(); //..【關注點代碼】
}
} finally{
HibernateSessionFactoryUtils.closeSession(session); ////..【關注點代碼】
}
}
複製代碼
切面:
public class AOP {
public void begin() {
System.out.println("開始事務");
}
public void close() {
System.out.println("關閉事務");
}
}
複製代碼
切入點:
切入點表達式:
1) 先引入aop相關jar文件 (aspectj aop優秀組件)
注意: 用到spring2.5版本的jar文件,若是用jdk1.7可能會有問題。
2) bean.xml中引入aop名稱空間
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
引入4個jar包:
<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
複製代碼
咱們以前手動的實現AOP編程是須要本身來編寫代理工廠的**,如今有了Spring,就不須要咱們本身寫代理工廠了。Spring內部會幫咱們建立代理工廠**。
所以,咱們只要關心切面類、切入點、編寫切入表達式指定攔截什麼方法就能夠了!
仍是以上一個例子爲案例,使用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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="aa"/>
<!-- 開啓aop註解方式 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
複製代碼
@Component
@Aspect//指定爲切面類
public class AOP {
//裏面的值爲切入點表達式
@Before("execution(* aa.*.*(..))")
public void begin() {
System.out.println("開始事務");
}
@After("execution(* aa.*.*(..))")
public void close() {
System.out.println("關閉事務");
}
}
複製代碼
@Component
public class UserDao implements IUser {
@Override
public void save() {
System.out.println("DB:保存用戶");
}
}
複製代碼
public interface IUser {
void save();
}
複製代碼
public class App {
public static void main(String[] args) {
ApplicationContext ac =
new ClassPathXmlApplicationContext("aa/applicationContext.xml");
//這裏獲得的是代理對象....
IUser iUser = (IUser) ac.getBean("userDao");
System.out.println(iUser.getClass());
iUser.save();
}
}
複製代碼
上面咱們測試的是UserDao有IUser接口,內部使用的是動態代理...那麼咱們此次測試的是目標對象沒有接口
@Component
public class OrderDao {
public void save() {
System.out.println("我已經進貨了!!!");
}
}
複製代碼
public class App {
public static void main(String[] args) {
ApplicationContext ac =
new ClassPathXmlApplicationContext("aa/applicationContext.xml");
OrderDao orderDao = (OrderDao) ac.getBean("orderDao");
System.out.println(orderDao.getClass());
orderDao.save();
}
}
複製代碼
@Aspect 指定一個類爲切面類
@Pointcut("execution( cn.itcast.e_aop_anno..(..))") 指定切入點表達式*
@Before("pointCut_()") 前置通知: 目標方法以前執行
@After("pointCut_()") 後置通知:目標方法以後執行(始終執行)
@AfterReturning("pointCut_()") 返回後通知: 執行方法結束前執行(異常不執行)
@AfterThrowing("pointCut_()") 異常通知: 出現異常時候執行
@Around("pointCut_()") 環繞通知: 環繞目標方法執行
測試:
// 前置通知 : 在執行目標方法以前執行
@Before("pointCut_()")
public void begin(){
System.out.println("開始事務/異常");
}
// 後置/最終通知:在執行目標方法以後執行 【不管是否出現異常最終都會執行】
@After("pointCut_()")
public void after(){
System.out.println("提交事務/關閉");
}
// 返回後通知: 在調用目標方法結束後執行 【出現異常不執行】
@AfterReturning("pointCut_()")
public void afterReturning() {
System.out.println("afterReturning()");
}
// 異常通知: 當目標方法執行異常時候執行此關注點代碼
@AfterThrowing("pointCut_()")
public void afterThrowing(){
System.out.println("afterThrowing()");
}
// 環繞通知:環繞目標方式執行
@Around("pointCut_()")
public void around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("環繞前....");
pjp.proceed(); // 執行目標方法
System.out.println("環繞後....");
}
複製代碼
咱們的代碼是這樣的:每次寫Before、After等,都要重寫一次切入點表達式,這樣就不優雅了。
@Before("execution(* aa.*.*(..))")
public void begin() {
System.out.println("開始事務");
}
@After("execution(* aa.*.*(..))")
public void close() {
System.out.println("關閉事務");
}
複製代碼
因而乎,咱們要使用@Pointcut這個註解,來指定切入點表達式,在用到的地方中,直接引用就好了!
@Component
@Aspect//指定爲切面類
public class AOP {
// 指定切入點表達式,攔截哪一個類的哪些方法
@Pointcut("execution(* aa.*.*(..))")
public void pt() {
}
@Before("pt()")
public void begin() {
System.out.println("開始事務");
}
@After("pt()")
public void close() {
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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--對象實例-->
<bean id="userDao" class="aa.UserDao"/>
<bean id="orderDao" class="aa.OrderDao"/>
<!--切面類-->
<bean id="aop" class="aa.AOP"/>
<!--AOP配置-->
<aop:config >
<!--定義切入表達式,攔截哪些方法-->
<aop:pointcut id="pointCut" expression="execution(* aa.*.*(..))"/>
<!--指定切面類是哪一個-->
<aop:aspect ref="aop">
<!--指定來攔截的時候執行切面類的哪些方法-->
<aop:before method="begin" pointcut-ref="pointCut"/>
<aop:after method="close" pointcut-ref="pointCut"/>
</aop:aspect>
</aop:config>
</beans>
複製代碼
public class App {
@Test
public void test1() {
ApplicationContext ac =
new ClassPathXmlApplicationContext("aa/applicationContext.xml");
OrderDao orderDao = (OrderDao) ac.getBean("orderDao");
System.out.println(orderDao.getClass());
orderDao.save();
}
@Test
public void test2() {
ApplicationContext ac =
new ClassPathXmlApplicationContext("aa/applicationContext.xml");
IUser userDao = (IUser) ac.getBean("userDao");
System.out.println(userDao.getClass());
userDao.save();
}
}
複製代碼
測試OrderDao
測試UserDao
切入點表達式主要就是來配置攔截哪些類的哪些方法
..咱們去文檔中找找它的語法...
在文檔中搜索:execution(
那麼它的語法是這樣子的:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
複製代碼
符號講解:
參數講解:
官方也有給出一些例子給咱們理解:
<!-- 【攔截全部public方法】 -->
<!--<aop:pointcut expression="execution(public * *(..))" id="pt"/>-->
<!-- 【攔截全部save開頭的方法 】 -->
<!--<aop:pointcut expression="execution(* save*(..))" id="pt"/>-->
<!-- 【攔截指定類的指定方法, 攔截時候必定要定位到方法】 -->
<!--<aop:pointcut expression="execution(public * cn.itcast.g_pointcut.OrderDao.save(..))" id="pt"/>-->
<!-- 【攔截指定類的全部方法】 -->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.*(..))" id="pt"/>-->
<!-- 【攔截指定包,以及其自包下全部類的全部方法】 -->
<!--<aop:pointcut expression="execution(* cn..*.*(..))" id="pt"/>-->
<!-- 【多個表達式】 -->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) || execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) or execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
<!-- 下面2個且關係的,沒有意義 -->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) && execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) and execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
<!-- 【取非值】 -->
<!--<aop:pointcut expression="!execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
複製代碼
若是文章有錯的地方歡迎指正,你們互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同窗,能夠關注微信公衆號:Java3y