Spring學習,依賴注入(DI)、控制反轉(IoC)和麵向切面(AOP)
阿新 • • 發佈:2019-01-08
依賴注入(DI)和控制反轉(IoC)
在日常編碼中經常遇到在一個類中呼叫另一個類的多個方法,所以就在一個類中new出需要用到的類,也就是組合模式。比如說A類裡面需要用到很多B類裡面的方法,所以要在A類裡面new一個B。
public class A {
private B b = new B();
public void aa(){
b.bb();
}
}
但是這樣有個弊端,就是類一開始就定死了,萬一業務有變化需要別的實現呢?所以我們要面向介面程式設計。比如說A類裡面需要B類,我們可以將B設計成介面,然後根據業務需求建立不同的B介面實現類B1,B2。。。這樣需要什麼樣的實現就new什麼例項。
package com.jyh.test;
public class A {
private B b = new B1();
public void aa(){
b.bb();
}
public interface B {
public void bb();
}
public class B1 implements B{
public void bb() {
}
}
public class B2 implements B{
public void bb() {
}
}
}
但是這樣還不好,這樣設計如果要改實現還要改A類裡面的程式碼替換B介面的實現類,我們可以將實現丟給呼叫者來實現,在A類裡面定義型別B的屬性,外部呼叫者可以通過setter方法和構造方法給該屬性賦值一個實現。
package com.jyh.test;
public class A {
private B b;
public A(B b) {
super();
this.b = b;
}
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
public void aa(){
b.bb();
}
public static interface B {
public void bb();
}
public static class B1 implements B{
public void bb() {
System.out.println("b1");
}
}
public static class B2 implements B{
public void bb() {
System.out.println("b2");
}
}
public static void main(String[] args) {
A a = new A(new B2());
a.aa();
}
}
這就是注入,將需要的實現通過某些方法給注入進去,依賴注入就是字面上的意思,因為A類的功能依賴B,所以注入B就稱作依賴注入,意思就是依賴的東西的注入。而IoC(控制反轉)的意思就是依賴的獲取被反轉了,本來A依賴B,所以A來建立一個例項,但是現在不是由A建立一個例項,而是別人建立好了例項傳遞給了A。
Spring容器中IoC部分就是做這種事,由Spring建立例項,由Spring進行注入。
Spring容器中的依賴注入(DI)和控制反轉(IoC)
xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
lazy-init,懶載入,在獲取bean的時候才建立例項
autowire:自動裝配,<property ref/>是指定裝配,autowire中byName值就是通過需要注入的屬性名字匹配對應id查詢bean
比如說本類中有個名為userDao的屬性,那麼就在這裡查詢id為userDao的bean來例項化
-->
<bean id="userService" class="com.jyh.service.impl.UserServiceImpl" lazy-init="true" autowire="byName">
<!-- property表示注入本類中的某個依賴,name為需要注入的依賴(屬性)的名字,
ref指向另一個bean的id,也就是說對應屬性的型別,如果是基本型別則就用value直接賦值
這個注入是setter方法注入 -->
<property name="userDao" ref="userDao" /><!-- bean -->
</bean>
<!-- dao -->
<bean id="userDao" class="com.jyh.dao.impl.UserDaoImpl"></bean>
<bean id="user" class="com.jyh.domain.User">
<!-- 基本型別用value直接賦值 -->
<property name="name" value="無名"></property>
</bean>
<!-- 構造方法注入 -->
<bean id="userServiceTwo" class="com.jyh.service.impl.UserServiceImpl">
<!-- 指向引數所帶表示的例項bean,順序按照構造方法引數的順序 -->
<constructor-arg ref="userDao"></constructor-arg>
<!-- 按照引數型別賦值
<constructor-arg type="java.lang.String" value="構造注入"/> -->
<!-- 按照引數名賦值
<constructor-arg name="years" value="7500000"/>
<constructor-arg name="ultimateAnswer" value="42"/> -->
<!-- 按照引數順序賦值
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/> -->
</bean>
</beans>
註解配置
編寫註解前需要在xml配置檔案中編寫如下配置
<!-- 將該類下的所有類配置成bean,只需要在該包下的類前面加上@Component(value = beanid)註解就可以不用再配置檔案中寫bean -->
<context:component-scan base-package="com.jyh.annotation"></context:component-scan>
<!-- 啟動註解 -->
<context:annotation-config></context:annotation-config>
註解
package com.jyh.annotation;
import javax.annotation.Resource;
/**
* 註解可以在屬性上面也可以在setter方法上面,建議在setter上面
*/
import org.springframework.stereotype.Component;
@Component(value = "b") //有這個註解就代表著在配置檔案中註冊了bean(等同於bean標籤),value值為bean的id,預設為首字母小寫的類名
public class B {
private String name;
//這兩個加一起等同於@Resource(name = "aaa")
//@Autowired //預設按照型別匹配
//@Qualifier(value = "aaa") //名稱匹配
private A a;
public String getName() {
return name;
}
//這個註解代表著這個屬性是要通過spring注入來的,等同於bean標籤下面的property標籤
//name屬性代表著該屬性在配置檔案中bean的id,也就是@Component()標籤中的value,沒寫預設為屬性名
@Resource(name = "a")
public void setName(String name) {
this.name = name;
}
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
}
面向切面(AOP)
砧板上放了一些面,一刀從中間切下,無論從面的這頭到那頭還是從那頭回到這頭都要經過刀面,刀面就是切面。
一個小區裡面的人出小區要經過保安,要進小區也要經過保安,保安可以檢視你是不是該小區的人,保安就是切面。
面向切面的應用:動態代理
JDK自帶的動態代理(藉助介面實現的動態代理)
//JDK自帶的動態代理實現,利用介面
@Test
public void proxy(){
final UserDao userDao2 = new UserDaoImpl();
UserDao userDao = (UserDao)Proxy.newProxyInstance(UserDaoImpl.class.getClassLoader(),
userDao2.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(proxy.getClass().getName());
System.out.println("動態代理");
method.invoke(userDao2,args);
return null;
}
});
userDao.say("nnn");
}
cglib的動態代理(不需要介面的動態代理)
//cglib實現動態代理,不需要介面
@Test
public void cglibProxy(){
final UserDao userDao2 = new UserDaoImpl();
Enhancer enhancer = new Enhancer();
//設定攔截器
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object arg0, Method arg1, Object[] arg2,
MethodProxy arg3) throws Throwable {
arg1.invoke(userDao2, arg2);
return null;
}
});
//設定傳入被代理的類進去作為父類
enhancer.setSuperclass(userDao2.getClass());
//獲取被代理類
UserDao userDao = (UserDao)enhancer.create();
userDao.say("cglib實現動態代理");
}
Spring容器中的面向切面
xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:component-scan base-package="com.jyh.annotation"></context:component-scan>
<!-- 啟動註解 -->
<context:annotation-config></context:annotation-config>
<!-- 註解aop -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!-- dao -->
<bean id="userDao" class="com.jyh.dao.impl.UserDaoImpl"></bean>
<!-- 切面類 -->
<bean id="myIntercept" class="com.jyh.aop.MyIntercept"></bean>
<!-- aop, proxy-target-class="true"設定用cglib實現代理 -->
<aop:config>
<!-- 切入點表示式 ,確定目標類,被代理的類,意思就是呼叫該類中的方法會觸發切面 -->
<aop:pointcut expression="execution(* com.jyh.dao.impl.UserDaoImpl.*(..))"
id="myProxy" />
<!-- ref指向的物件就是切面,也就是公共呼叫的處理方法所在的類 -->
<aop:aspect ref="myIntercept">
<!-- 方法呼叫前的處理,method指向方法,pointcut-ref指向代理物件,也就是上面pointcut元素的id -->
<aop:before method="beforeMethod" pointcut-ref="myProxy" />
<!-- 方法呼叫後的處理 -->
<aop:after-returning method="afterMethod"
pointcut-ref="myProxy" returning="val" />
<!-- 無論有沒有成功執行,最終都會呼叫這個方法 -->
<aop:after method="finallyMethod" pointcut-ref="myProxy" />
<!-- 異常通知 -->
<aop:after-throwing method="exceptionMethod"
throwing="ex" pointcut-ref="myProxy" />
<!-- 環繞通知,能控制目標方法執行不執行 -->
<aop:around method="aroundMethod" pointcut-ref="myProxy" />
</aop:aspect>
</aop:config>
</beans>
package com.jyh.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyIntercept {
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("連線點的名稱(方法名稱):"+methodName);
System.out.println("目標類:"+joinPoint.getTarget().getClass());
System.out.println("方法呼叫前");
System.out.println("-------------------");
}
public void afterMethod(JoinPoint joinPoint,Object val){
System.out.println("目標方法的返回值:" + val);
System.out.println("方法呼叫後");
System.out.println("-------------------");
}
public void finallyMethod(){
System.out.println("最後始終會執行的方法");
System.out.println("-------------------");
}
public void exceptionMethod(JoinPoint joinPoint,Throwable ex){
System.out.println("異常執行的方法");
System.out.println(ex.getMessage());
System.out.println("-------------------");
}
public void aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("環繞控制前");
joinPoint.proceed();//呼叫目標方法
System.out.println("環繞控制後");
System.out.println("-------------------");
}
}
註解配置
<context:component-scan base-package="com.jyh.annotation"></context:component-scan>
<!-- 啟動註解 -->
<context:annotation-config></context:annotation-config>
<!-- 註解aop -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
package com.jyh.annotation;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* @Before方法執行前 @AfterReturning方法執行後 @After最終始終都會執行的方法
* @AfterThrowing異常執行的方法 @Around環繞
* @author OverrideRe
*
*/
@Component("D")
@Aspect //表示本類是切面
public class D {
//切入點,由於切入點都是方法,所以這裡也定義一個方法作為切入點,沒有實際意義
@Pointcut("execution(* com.jyh.annotation.C.*(..))")
private void cc(){};
@Before("cc()")
public void before(){
System.out.println("方法前");
}
@AfterReturning("cc()")
public void after(){
System.out.println("方法後");
}
}
Spring整合hibernate和AOP應用之宣告式事務
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 連結資料庫的使用者名稱 -->
<property name="connection.username">root</property>
<!-- 連結資料庫的密碼 -->
<property name="connection.password">ying1995520***</property>
<!-- 連結資料庫的驅動 -->
<property name="connection.driver_class">
com.mysql.jdbc.Driver
</property>
<!-- 連結資料庫的url -->
<property name="connection.url">
jdbc:mysql://localhost:3306/db3
</property>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hbm2ddl.auto">update</property>
<!-- 顯示sql語句 -->
<property name="show_sql">true</property>
<!-- 格式化的顯示sql語句 -->
<property name="format_sql">true</property>
<mapping resource="com/jyh/domain/Account.hbm.xml"/>
</session-factory>
</hibernate-configuration>