一.AOP(Aspect Oriented Programing)面向切面編程
AOP的終極目標:讓我們可以專心做事
下面通過一個例子來介紹AOP的具體使用
案例的要求:使用AOP實現日志記錄系統 , 核心模塊 和 增強 單獨 開發 ,運行時再組裝
首先定義接口和方法
接口和實現類中的代碼,我放在一起了,應該比較簡單
package demo04.dao; /** * Created by mycom on 2018/3/5. */ public interface IHelloDao { public void doSome(); }
package demo04.dao;
/**
* Created by mycom on 2018/3/5.
*/
public class HelloDaoImpl implements IHelloDao {
public void doSome() {
System.out.println("已經成功加入到DB中了");
}
}
package demo04.service;
/**
* Created by mycom on 2018/3/5.
*/
public interface IHelloService {
public void doSome();
}
package demo04.service;
import demo04.dao.IHelloDao;
/**
* Created by mycom on 2018/3/5.
*/
public class HelloServiceImpl implements IHelloService {
//創建一個Dao的對象
IHelloDao dao;
public IHelloDao getDao() {
return dao;
}
public void setDao(IHelloDao dao) {
this.dao = dao;
}
public void doSome() {
dao.doSome();
}
}
同樣在resources下面也要有一個xml文件----applicationContext.xml
<!--返回的類型只能是實現類--> 這里需要注意一下 class的值只能是實現類的包 <bean id="dao" class="demo04.dao.HelloDaoImpl"> </bean> <bean id="service" class="demo04.service.HelloServiceImpl"> <property name="dao" ref="dao"></property> </bean>
然后編寫測試類進行測試
@Test public void t1(){ ApplicationContext context=new ClassPathXmlApplicationContext("applicationContextAop.xml"); //這里的返回值只能是接口 IHelloService service =(IHelloService) context.getBean("service"); service.doSome(); }
運行的結果
現在我們要在這句話出現之前,先記錄一下日志,出現之后,再出現一句話
首先要創建一個新的包AOP包,並且在包下面寫兩個類
LoggerAfter是后置增強
LoggerBefore是前置增強
這兩個類中的代碼如下
package demo04.aop; import org.springframework.aop.AfterReturningAdvice; import java.lang.reflect.Method; /** * Created by mycom on 2018/3/5. */ public class LoggerAfter implements AfterReturningAdvice { public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable { System.out.println("=======after"); } }
package demo04.aop; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; /** * Created by mycom on 2018/3/5. */ public class LoggerBefore implements MethodBeforeAdvice { public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println("日志記錄"); } }
在xml中配置,在配置xml是要給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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="service" class="demo04.service.HelloServiceImpl"> <property name="dao" ref="dao"></property> </bean> <!--前置--> <bean id="beforeAdvice" class="demo04.aop.LoggerBefore"> </bean> <!--后置--> <bean id="afterAdvice" class="demo04.aop.LoggerAfter"> </bean> <!--配置aop--> <aop:config> <!--切點--> <aop:pointcut id="mypoint" expression="execution(public void demo04.service.HelloServiceImpl.doSome())"></aop:pointcut> <!--<aop:pointcut id="mypoint" expression="execution(* *..service.*.*(..))"></aop:pointcut>--> <!--advice-ref:做什么樣的配置,是前置還是后置 pointcut-ref:鎖定什么樣的方法規則,那個方法需要被鎖定 --> <aop:advisor advice-ref="beforeAdvice" pointcut-ref="mypoint"></aop:advisor> <aop:advisor advice-ref="afterAdvice" pointcut-ref="mypoint"></aop:advisor> </aop:config>
測試類
import demo04.service.HelloServiceImpl; import demo04.service.IHelloService; import demo05.MyCollection; import demo05.Student; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Created by mycom on 2018/3/3. */ public class Test20180305 { @Test public void t1(){ ApplicationContext context=new ClassPathXmlApplicationContext("applicationContextAop.xml"); //這里的返回值只能是接口 IHelloService service =(IHelloService) context.getBean("service"); service.doSome(); } }
在這里和要介紹兩個單詞的意思
advice :通知
advisor:顧問
顧問可以包裝通知
execution(【modifiers-pattern?】 訪問修飾符
ret-type-pattern 返回值類型
【declaring-type-pattern?】 全限定性類名
name-pattern(param-pattern) 方法名(參數名) 包名.類型名.方法名
【throws-pattern?】) 拋出異常類型
public void doLog(String log){
}
方法簽名
切入點表達式要匹配的對象就是目標方法的方法名。所以,execution表達式中明顯就是方法的簽名。
注意:表達式中加[]的部分表示可省略部分,各部分間用空格分開。在其中可以使用以下符號:
符號 意義
* 0至多個任意字符
.. 用在方法參數中,表示任意多個參數
用在包名后,表示當前包及其子包路徑
+ 用在類名后,表示當前類及其子類
用在接口后,表示當前接口及其實現類
案例:
execution(public * *(..)) 指定切入點為:任意公共方法
execution(* set*(..)) 指定切入點為:任何一個以"set"開始的方法
二.給屬性注入值(四種)
1.setter方法注入(就是在bean節點下有property節點給里面的屬性值賦值
<bean id="service" class="demo04.service.HelloServiceImpl"> <property name="dao" ref="dao"></property> </bean>
2.構造注入
3.p命名空間注入(使用前要先要在Spring配置文件中引入p命名空間xmlns:p="http://www.springframework.org/schema/p")
這里的兩種我寫在一起了
首先寫一個學生類,車類,MyCollection
package demo05; /** * Created by mycom on 2018/3/5. */ public class Student { private String name; private Integer age; private Car car; public Student() { } public Student(String name, Integer age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } }
package demo05; /** * Created by mycom on 2018/3/5. */ public class Car { private String color; private String brand; public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } }
在xml中的配置
<!--構造注入--> <!--<bean id="stu" class="demo05.Student"> <constructor-arg index="0" value="小明"></constructor-arg> <constructor-arg index="1" value="12"></constructor-arg> </bean>--> <!--p命名空間注入--> <bean id="stu" class="demo05.Student" p:name="小明" p:age="12" p:car-ref="car"></bean>
最后編寫測試類
以p命名空間注入為例進行測試
import demo04.service.HelloServiceImpl; import demo04.service.IHelloService; import demo05.MyCollection; import demo05.Student; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Created by mycom on 2018/3/3. */ public class Test20180305 { @Test public void t1(){ ApplicationContext context=new ClassPathXmlApplicationContext("applicationContextCon.xml");
Student service2 =(Student) context.getBean("stu");
System.out.println(service2.getName());
} }
4.集合注入
再寫一個MyCollection類
package demo05; import java.util.*; /** * Created by mycom on 2018/3/5. */ public class MyCollection { private String[] array; private List<String> list; private Set<String> set; private Map<String,String> map; private Properties properties; public MyCollection() { System.out.println("創建對象==========="); } public MyCollection(String[] array, List<String> list, Set<String> set, Map<String, String> map, Properties properties) { this.array = array; this.list = list; this.set = set; this.map = map; this.properties = properties; } @Override public String toString() { return "MyCollection{" + "array=" + Arrays.toString(array) + ", list=" + list + ", set=" + set + ", map=" + map + ", properties=" + properties + '}'; } public String[] getArray() { return array; } public void setArray(String[] array) { this.array = array; } public List<String> getList() { return list; } public void setList(List<String> list) { this.list = list; } public Set<String> getSet() { return set; } public void setSet(Set<String> set) { this.set = set; } public Map<String, String> getMap() { return map; } public void setMap(Map<String, String> map) { this.map = map; } public Properties getProperties() { return properties; } public void setProperties(Properties properties) { this.properties = properties; } }
<!--集合注入--> <bean id="collection" class="demo05.MyCollection" scope="prototype"> <!--Array--> <property name="array"> <array> <value>小明</value> <value>小蘭</value> </array> </property> <!--list--> <property name="list"> <list> <value>list1</value> <value>list2</value> </list> </property> <!--set--> <property name="set"> <set> <value>set1</value> <value>set2</value> </set> </property> <!--map--> <property name="map"> <map> <entry key="1"> <value>01</value> </entry> <entry key="2"> <value>02</value> </entry> </map> </property> <!--propreties--> <property name="properties"> <props> <prop key="001">001</prop> <prop key="002">002</prop> </props> </property> </bean>
import demo04.service.HelloServiceImpl; import demo04.service.IHelloService; import demo05.MyCollection; import demo05.Student; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Created by mycom on 2018/3/3. */ public class Test20180305 { @Test public void t1(){ ApplicationContext context=new ClassPathXmlApplicationContext("applicationContextCon.xml"); //返回的類型只能是接口 MyCollection service =(MyCollection) context.getBean("collection"); System.out.println(service); } }
三.Spring中bean是單例的問題
單例:一個類在內存中只能有一個對象。
單利滿足的三個條件:
1.構造私有
2.是有的靜態變量,變量的類型就是當前類的類型
3.提供一個靜態方法
在xml中配置實現單例還是多例的屬性是scope
他的值有兩個singleton 單例
prototype 多例
在上面的集合注入的例子中進行改動,並且如果MyCollection中有toString方法,也要注釋掉,可以更直觀的看到,如下
然后我們來編寫測試方法進行測試
import demo04.service.HelloServiceImpl; import demo04.service.IHelloService; import demo05.MyCollection; import demo05.Student; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Created by mycom on 2018/3/3. */ public class Test20180305 { @Test public void t1(){ ApplicationContext context=new ClassPathXmlApplicationContext("applicationContextCon.xml"); MyCollection service =(MyCollection) context.getBean("collection"); System.out.println(service); MyCollection service2 =(MyCollection) context.getBean("collection"); System.out.println(service2); } }
運行結果
從運行結果來看,兩次創建的對象的指針不同,創建的是兩個對象,這是多例
線面再來看單例,將scope的屬性值改為singleton
此時的運行結果如下
他只創建了依次對象,實現了單利