以前沒學spring的時候開發項目是這樣的
- 實體類—>class User{ }
- daoclass–> UserDao{ … 訪問db}
- service—>class UserService{ UserDao userDao = new UserDao();}
- actionclass UserAction{UserService userService = new UserService();}
用戶訪問的步驟是
Tomcat->action->service->dao
springIoc
所謂的依賴注入,則是,甲方開放接口,在它需要的時候,能夠講乙方傳遞進來(注入)
所謂的控制反轉,甲乙雙方不相互依賴,交易活動的進行不依賴於甲乙任何一方,整個活動的進行由第三方負責管理。
Spring容器不單單隻有一個,可以歸爲兩種類型
Bean工廠,BeanFactory【功能簡單】
應用上下文,ApplicationContext【功能強大,一般我們使用這個】
講解一下bean是啥?
Java面向對象,對象有方法和屬性,那麼就需要對象實例來調用方法和屬性(即實例化);
凡是有方法或屬性的類都需要實例化,這樣才能具象化去使用這些方法和屬性;
關於bean的註解們:
1、一類是使用Bean,即是把已經在xml文件中配置好的Bean拿來用,完成屬性、方法的組裝;比如@Autowired , @Resource,可以通過byTYPE(@Autowired)、byNAME(@Resource)的方式獲取Bean;
2、一類是註冊Bean,@Component , @Repository , @ Controller , @Service , @Configration這些註解都是把你要實例化的對象轉化成一個Bean,放在IoC容器中,等你要用的時候,它會和上面的@Autowired , @Resource配合到一起,把對象、屬性、方法完美組裝。
示例:
通過Resource獲取BeanFactory
加載Spring配置文件
通過XmlBeanFactory+配置文件來創建IOC容器
在Spring中總體來看可以通過三種方式來配置對象:
使用XML文件配置
使用註解來配置
使用JavaConfig來配置
無參創建對象
/**
* Created by ozc on 2017/5/10.
*/
public class User {
private String id;
private String username;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
//idea的applicationContext.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">
</beans>
<bean id="user" class="User"/>
// 得到IOC容器對象
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) ac.getBean("user");
System.out.println(user);
帶參創建對象
public User(String id, String username) {
this.id = id;
this.username = username;
}
<bean id="user" class="User">
<!--通過constructor這個節點來指定構造函數的參數類型、名稱、第幾個-->
<constructor-arg index="0" name="id" type="java.lang.String" value="1"></constructor-arg>
<constructor-arg index="1" name="username" type="java.lang.String" value="zhongfucheng"></constructor-arg>
</bean>
注意在constructor上如果構造函數的值是一個對象,而不是一個普通類型的值,我們就需要用到ref屬性了,而不是value屬性,比如說:我在User對象上維護了Person對象的值,想要在構造函數中初始化它。因此,就需要用到ref屬性了
<bean id="person" class="Person"></bean>
<bean id="user" class="User" >
<!--通過constructor這個節點來指定構造函數的參數類型、名稱、第幾個-->
<constructor-arg index="0" name="id" type="java.lang.String" value="1"></constructor-arg>
<constructor-arg index="1" name="username" type="java.lang.String" ref="person"></constructor-arg>
</bean>
配置文件用靜態方法來返回
<bean id="user" class="Factory" factory-method="getBean" >
</bean>
public class Factory {
public static User getBean() {
return new User();
}
}
//表明該類是配置類
@Configuration
//啓動掃描器,掃描bb包下的
//也可以指定多個基礎包
//也可以指定類型
@ComponentScan("bb")
public class AnnotationScan {
}
@ComponentScan掃描器
@Configuration表明該類是配置類
@Component 指定把一個對象加入IOC容器--->@Name也可以實現相同的效果【一般少用】
@Repository 作用同@Component; 在持久層使用
@Service 作用同@Component; 在業務邏輯層使用
@Controller 作用同@Component; 在控制層使用
@Resource 依賴關係
如果@Resource不指定值,那麼就根據類型來找,相同的類型在IOC容器中不能有兩個
如果@Resource指定了值,那麼就根據名字來找
singleton【單例】,從IOC容器獲取的對象都是同一個
prototype【多例】,從IOC容器獲取的對象都是不同的
如果我們想要對象在創建後,執行某個方法,我們指定爲init-method屬性就行了。。
如果我們想要IOC容器銷燬後,執行某個方法,我們指定destroy-method屬性就行了。
<bean id="user" class="User" scope="singleton" lazy-init="true" init-method="" destroy-method=""/>
springAop
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();
}
}
但是這樣還不夠好
不如用以下的代理模式 aop將這部分切割下來,重複使用
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;
}
}
);
}
}
靜態工廠方法
把AOP加入IOC容器中
//把該對象加入到容器中
@Component
public class AOP {
public void begin() {
System.out.println("開始事務");
}
public void close() {
System.out.println("關閉事務");
}
}
把UserDao放入容器中
@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>
測試
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>
重複代碼就叫做關注點。
// 保存一個用戶
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("關閉事務");
}
}
切入點:
執行目標對象方法,動態植入切面代碼。可以通過切入點表達式,指定攔截哪些類的哪些方法; 給指定的類在運行的時候植入切面類代碼。
切入點表達式:指定哪些類的哪些方法被攔截
有了Spring,就不需要我們自己寫代理工廠了。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: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("關閉事務");
}
}
UserDao實現了IUser接口
@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();
}
}
@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這個註解,來指定切入點表達式,在用到的地方中,直接引用就行了!
那麼我們的代碼就可以改造成這樣了:
```go
@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("關閉事務");
}
}