先整理AOP之前, 我先把之前DI的內(nèi)容整理完!
DI
Spring的依賴注入第二種方式:
通過構(gòu)造方法注入屬性
首先提供一個(gè)實(shí)體類, 不過這個(gè)類沒有屬性的get和set方法, 只有一個(gè)有參的構(gòu)造方法和toString方法.
public class Boy {
private String id;
private String name;
private Integer age;
private Double salary;
public Boy(String id, String name, Integer age, Double salary) {
System.out.println("IOC容器創(chuàng)建對(duì)象");
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
@Override
public String toString() {
return "Boy{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", age=" + age +
", salary=" + salary +
'}';
}
}
Spring配置
<!--
通過constructor實(shí)現(xiàn)依賴注入
constructor-arg: 通過構(gòu)造方法傳過去的值
type: 用來限制傳遞的參數(shù)的類型
-->
<bean id="boyId" class="com.wtu.spring.di.constructor.Boy">
<!--
<constructor-arg type="java.lang.String">
<value>001</value>
</constructor-arg>
<constructor-arg type="java.lang.String">
<value>小陽</value>
</constructor-arg>
<constructor-arg type="java.lang.Integer">
<value>16</value>
</constructor-arg>
<constructor-arg type="java.lang.Double">
<value>1000</value>
</constructor-arg>
-->
<!-- index: 參數(shù)在構(gòu)造方法中的位置, 第一個(gè)參數(shù)從0開始 -->
<constructor-arg index="0">
<value>001</value>
</constructor-arg>
<constructor-arg index="1">
<value>小陽</value>
</constructor-arg>
<constructor-arg index="2">
<value>16</value>
</constructor-arg>
<constructor-arg index="3">
<value>1000</value>
</constructor-arg>
</bean>
測(cè)試類:
// 啟動(dòng)IOC容器
ApplicationContext ac = new ClassPathXmlApplicationContext(
new String[]{"com/wtu/spring/di/constructor/spring3.0.xml"}
);
Boy boy = (Boy) ac.getBean("boyId");
System.out.println(boy);
//Boy{id='001', name='小陽', age=16, salary=1000.0}
注入Date類型:
在依賴注入中 如何將一個(gè)字符串轉(zhuǎn)換成Date對(duì)象, 注入到目標(biāo)對(duì)象
public class Boy {
private String id;
private String name;
private Integer age;
private Date birthday;
private Double salary;
public Boy() {
System.out.println("Ioc容器創(chuàng)建對(duì)象");
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
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 Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Boy{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", birthday=" + birthday +
", age=" + age +
", salary=" + salary +
'}';
}
}
Spring配置
<!-- 注冊(cè)時(shí)間格式轉(zhuǎn)換器
-->
<bean id="format" class="java.text.SimpleDateFormat">
<constructor-arg>
<!-- 時(shí)間的格式樣式 -->
<value>yyyy-MM-dd</value>
</constructor-arg>
</bean>
<!-- 注冊(cè)bean對(duì)象
new SimpleDateFormat("yyyy-MM-dd").parse("2017-12-12");
-->
<bean id="boy" class="com.wtu.spring.di.set.Boy">
<property name="birthday">
<bean factory-bean="format" factory-method="parse">
<constructor-arg>
<value>2017-12-12</value>
</constructor-arg>
</bean>
</property>
</bean>
測(cè)試類:
// 啟動(dòng)IOC容器
ApplicationContext ac = new ClassPathXmlApplicationContext(
new String[]{"com/wtu/spring/di/set/spring3.0.xml"}
);
Boy boy = (Boy) ac.getBean("boy");
System.out.println(new SimpleDateFormat("yyyy-MM-dd").parse("2017-12-12"));
//Tue Dec 12 00:00:00 CST 2017
System.out.println(boy);
//Boy{id='null', name='null', birthday=Tue Dec 12 00:00:00 CST 2017, age=null, salary=null}
DI注解形式
UserDaoImpl.java:
public class UserDaoImpl implements UserDao {
public UserDaoImpl() {
System.out.println("IOC容器創(chuàng)建對(duì)象");
}
@Override
public void addUser() {
System.out.println("添加用戶");
}
}
UserServiceImpl.java:
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl() {
System.out.println("IOC容器創(chuàng)建UserServiceImpl對(duì)象");
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void addUser() {
System.out.println("service中addUser方法被調(diào)用了");
}
}
Spring配置:
<!--注冊(cè)UserDao對(duì)象 -->
<bean id="userDao" class="com.wtu.spring.di.annotation.UserDaoImpl"/>
<!--注冊(cè)UserService對(duì)象 -->
<bean id="userService" class="com.wtu.spring.di.annotation.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
測(cè)試類:
// 啟動(dòng)IOC容器
ApplicationContext ac = new ClassPathXmlApplicationContext(
new String[]{"com/wtu/spring/di/annotation/spring3.0.xml"}
);
UserService us = (UserService) ac.getBean("userService");
運(yùn)行結(jié)果:
注解改寫:
后來的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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 開啟Spring注解的功能 -->
<context:annotation-config/>
<!-- 讓springIOC容器自動(dòng)去掃描某一個(gè)包, 創(chuàng)建該包下的bean對(duì)象 -->
<context:component-scan base-package="com.wtu.spring.di.annotation2"/>
</beans>
UserDaoImpl.java:
/**
* @Component(value = "userDao")
* 告訴SpringIOC容器創(chuàng)建一個(gè)id為userDao的bean對(duì)象
* value可以省略, 但是此時(shí)生成的id為類名, 并且首字母小寫
*/
//@Component(value = "userDao") // userDaoImpl
@Repository(value = "userDao")
public class UserDaoImpl implements UserDao {
public UserDaoImpl() {
System.out.println("IOC容器創(chuàng)建對(duì)象");
}
@Override
public void addUser() {
System.out.println("添加用戶");
}
}
UserServiceImpl.java:
@Service(value = "userServiceImpl")
public class UserServiceImpl implements UserService {
@Resource(name = "userDao")
// @Autowired
private UserDao userDao;
public UserServiceImpl() {
System.out.println("IOC容器創(chuàng)建UserServiceImpl對(duì)象");
}
/*
spring會(huì)自動(dòng)查找id為userDao的bean對(duì)象, 如果找到就通過下面的set方法注入進(jìn)來
*/
// @Resource(name = "userDao")
// public void setUserDao(UserDao userDao) {
// this.userDao = userDao;
// }
@Override
public void addUser() {
System.out.println("service中addUser方法被調(diào)用了");
}
}
測(cè)試類:
// 啟動(dòng)IOC容器
ApplicationContext ac = new ClassPathXmlApplicationContext(
new String[]{"com/wtu/spring/di/annotation2/spring3.0.xml"}
);
UserService us = (UserService) ac.getBean("userServiceImpl");
us.addUser();
運(yùn)行結(jié)果:
注意:
@Component
現(xiàn)在不提倡使用對(duì)于注冊(cè)bean對(duì)象的注解現(xiàn)在提倡使用如下三個(gè):
@Controller
一般作用在springMVC中@Service
作用在業(yè)務(wù)層@Repository
作用在持久層但是不是絕對(duì)的, 任何一個(gè)在任何一層都可以用
AOP
如圖上述的解決方案不好:
在javaweb三層框架中辆飘,dao層是數(shù)據(jù)訪問層,它的核心業(yè)務(wù)就是操作數(shù)據(jù)庫栅螟,然而像輸出時(shí)間,開啟事務(wù), 日志輸出等等。這些事情都不是核心業(yè)務(wù),我們可以稱之為服務(wù)代碼毁涉,我們不能將執(zhí)行服務(wù)代碼這樣的事情交給dao層來做。但是我們又需要這樣的服務(wù)代碼锈死。所以我們可以找該類的一個(gè)代理對(duì)象來實(shí)現(xiàn)贫堰。
動(dòng)態(tài)代理圖解:
動(dòng)態(tài)代理核心代碼模擬實(shí)現(xiàn)
日志類, 切面:
public class MyLog {
@SuppressWarnings("deprecation")
public void writeConsole() {
System.out.println(new Date().toLocaleString());
}
}
目標(biāo)對(duì)象所在類, 執(zhí)行核心代碼:
public class UserDaoImpl implements UserDao {
@Override
public void addUser() {
System.out.println("添加用戶...");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void deleteUser() {
System.out.println("刪除用戶");
}
}
模擬JDK動(dòng)態(tài)代理的Proxy類:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 中間類, 相當(dāng)于JDK動(dòng)態(tài)代理中的Proxy
* @Author menglanyingfei
* @Created on 2018.01.17 16:51
*/
public class Middle {
private UserDao userDao = new UserDaoImpl();
// 定義切面
private MyLog myLog = new MyLog();
public Object getObject() {
/*
第一個(gè)參數(shù): 當(dāng)前類的類加載器
第二個(gè)參數(shù): 代理對(duì)象實(shí)現(xiàn)的接口類型
第三個(gè)參數(shù): 是個(gè)接口, 在這個(gè)接口中具體去處理如何調(diào)用切面以及目標(biāo)對(duì)象
匿名內(nèi)部類
*/
return Proxy.newProxyInstance(
this.getClass().getClassLoader(),
userDao.getClass().getInterfaces(),
new InvocationHandler() {
/**
*
* @param proxy 目標(biāo)對(duì)象
* @param method 需要執(zhí)行目標(biāo)對(duì)象的方法對(duì)應(yīng)的Method對(duì)象
* @param args 執(zhí)行目標(biāo)對(duì)象業(yè)務(wù)方法時(shí)需要的參數(shù)
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 定義返回值, 目標(biāo)對(duì)象方法執(zhí)行以后的返回值
Object result = "";
// 獲取目標(biāo)對(duì)象的方法名
String methodName = method.getName();
if ("addUser".equals(methodName)) {
myLog.writeConsole();
result = method.invoke(userDao, args);
myLog.writeConsole();
} else {
result = method.invoke(userDao, args);
}
return result;
}
}
);
}
}
測(cè)試類:
// 通過一個(gè)中間類來獲取代理對(duì)象
Middle middle = new Middle();
UserDao userDao = (UserDao) middle.getObject();
userDao.addUser();
System.out.println(userDao);
//com.wtu.spring.aop.base.UserDaoImpl@5305068a
// 紅色標(biāo)識(shí)顯示
System.err.println(userDao.getClass());
// 使用JDK動(dòng)態(tài)代理產(chǎn)生的代理對(duì)象
//class com.sun.proxy.$Proxy0
運(yùn)行結(jié)果:
導(dǎo)入aop的所需jar包:
SpringAOP的五種通知方式
通知就是告訴代理在什么時(shí)候去執(zhí)行服務(wù)代碼。
1.前置通知: 執(zhí)行目標(biāo)對(duì)象業(yè)務(wù)方法之前執(zhí)行服務(wù)代碼(切面)
2.后置通知: 執(zhí)行目標(biāo)對(duì)象業(yè)務(wù)方法之后執(zhí)行服務(wù)代碼
3.方法正常返回通知:
4.方法拋出異常通知:
5.環(huán)繞通知:
由于晚上太想睡覺了, 所以只先總結(jié)到前置通知, 把剩下的內(nèi)容留到下次總結(jié)吧!
先弄一下前置通知吧!
先定義一個(gè)切面: 輸出日志時(shí)間類
package com.wtu.spring.aop.before;
import java.util.Date;
/**
* 切面
* @Author menglanyingfei
* @Created on 2018.01.17 17:06
*/
public class MyLog {
@SuppressWarnings("deprecation")
public void writeConsole() {
System.out.println(new Date().toLocaleString());
}
}
目標(biāo)對(duì)象所在類的核心業(yè)務(wù)代碼:
public class UserDaoImpl implements UserDao {
@Override
public void addUser() {
// if (true) {
// throw new RuntimeException("測(cè)試一下遇到異常執(zhí)行情況");
// }
System.out.println("添加用戶...");
}
@Override
public void deleteUser() {
System.out.println("刪除用戶");
}
}
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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
">
<!-- 注冊(cè)UserDao對(duì)象 -->
<bean id="userDao" class="com.wtu.spring.aop.before.UserDaoImpl">
</bean>
<!-- 配置切面 -->
<bean id="myLog" class="com.wtu.spring.aop.before.MyLog"/>
<!--
aop:config: 配置代理對(duì)象
proxy-target-class: 指底層的動(dòng)態(tài)代理使用的是JDK, 還是CGLIB代理;
true表示CGLIB代理, CGLIB代理不是說沒有接口, spring會(huì)根據(jù)目標(biāo)對(duì)象自動(dòng)生成一個(gè)接口
aop:pointcut: 配置切入點(diǎn)
execution: 確定切入點(diǎn)的方法
aop:aspect: 配置切面如何去切切入點(diǎn)
pointcut-ref: 切面中的方法去切哪個(gè)目標(biāo)對(duì)象中的切入點(diǎn)
-->
<aop:config proxy-target-class="true">
<aop:pointcut expression="execution(* com.wtu.spring.aop.before.UserDaoImpl.a*(..))" id="xxx"/>
<aop:aspect ref="myLog">
<aop:before method="writeConsole" pointcut-ref="xxx"/>
</aop:aspect>
</aop:config>
</beans>
測(cè)試類:
package com.wtu.spring.aop.before;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @Author menglanyingfei
* @Created on 2018.01.17 14:22
*/
public class TestSpringIoc {
public static void main(String[] args) {
// 啟動(dòng)Ioc容器
ApplicationContext ac = new ClassPathXmlApplicationContext(
new String[]{"com/wtu/spring/aop/before/spring3.0.xml"}
);
UserDao userDao = (UserDao) ac.getBean("userDao");
userDao.addUser();
System.out.println(userDao.getClass());
//class com.wtu.spring.aop.before.UserDaoImpl$$EnhancerByCGLIB$$aa48c241
}
}
運(yùn)行結(jié)果:
完整代碼地址見Github
https://github.com/menglanyingfei/SSMLearning/tree/master/spring_day02