spring入門及常用註解的使用

目前java主流的開源框架無論是ssh還是ssi,除了spring,其他的框架都有可替換的框架struts2和springmvc,hibernate和ibatis(mybatis),這裏我們不討論其他的框架的優劣。那爲什麼spring這麼受開發者的歡迎呢?那就是spring的優點,博主以爲spring的優點有:

1.輕量級:你可以選擇你想要的服務,還有代碼的低侵入性。

2.控制反轉ioc:Spring使用控制反轉技術實現了鬆耦合。依賴被注入到對象,而不是創建或尋找依賴對象。

3.面向切面編程aop:Spring支持面向切面編程,同時把應用的業務邏輯與系統的服務分離開來。

4.事務管理:spring有着強大的事務處理能力。

5.集成性:能很好的集成其他的框架。

那下面我們結合着代碼依次來說說spring的各個有點

一:輕量級

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.julyday</groupId>
	<artifactId>myspring</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>myspring</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<spring.version>4.1.4.RELEASE</spring.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>

		<!-- spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
		</dependency>
	</dependencies>
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework</groupId>
				<artifactId>spring-framework-bom</artifactId>
				<version>${spring.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

</project>
上面是我們需要用到的spring的模塊,就三個,我們再看看jar包,包括junit4一共才9個jar包,是不是很優雅。

二:控制反轉ioc

OK,下面用博主請客吃飯來講spring簡單的例子。
首先請客吃飯少不要人了,我們建一個人類的接口:
public interface Person {

	public void eat();
	
}
接着是作者本人這個類:
public class Author implements Person {
	
	@Override
	public void eat() {
		System.out.println("作者喜歡吃肉");
	}
	
}
建一個spring-bean.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" >
        
        <bean id="author" class="com.julyday.myspring.Author" ></bean>
 </beans>    
測試下:
BeanTest.java:
package com.julyday.myspring;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;

@RunWith(BlockJUnit4ClassRunner.class)
public class BeanTest extends BaseTest{
	
	@Test
	public void testBean(){
		Person author = (Person)context.getBean("author");
		author.eat();
	}
}

BaseTest.java:
package com.julyday.myspring;

import org.junit.After;
import org.junit.Before;
import org.springframework.beans.BeansException;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BaseTest {
	public ClassPathXmlApplicationContext context;
	
	@Before
	public void before() {
		try {
			context = new ClassPathXmlApplicationContext("spring-bean.xml");
			context.start();
		} catch (BeansException e) {
			e.printStackTrace();
		}
	}
	
	@After
	public void after() {
		context.destroy();
	}
}
選中testBean方法右鍵運行Junit Test,運行成功。

作者一個人吃飯沒意思,找了其他朋友一起來。
第一個是個美國人:
public class American implements Person{

	@Override
	public void eat() {
		System.out.println("美國人喜歡吃牛肉");
	}

}
spring-bean.xml的beans裏面增加:

<bean id="american" class="com.julyday.myspring.American" scope="prototype"></bean>

這裏加了一個scope,簡單來說不加這個標識的就是singleton(單例的,
prototype這個呢就是每次請求(包括在注入在其他bean裏面的,或者spring容器的getBean()方法)。
當然在web項目裏面還有
request(一個http請求一個),
session(一次session回話一個),
global session(似於標準的HTTP Session作用域,不過它僅僅在基於portlet的web應用中才有意義)。
大多數時候我們還是用的單例。

下面我們建一個測試類測試下:
ScopeTest.java
@RunWith(BlockJUnit4ClassRunner.class)
public class ScopeTest extends BaseTest{
	
	@Test
	public void testBean(){
		Person p1 = (Person)context.getBean("author");
		p1.eat();
		Person p2 = (Person)context.getBean("author");
		p2.eat();
		System.out.println(p1.hashCode());
		System.out.println(p2.hashCode());
		
		
		Person p3 = (Person)context.getBean("american");
		p3.eat();
		Person p4 = (Person)context.getBean("american");
		p4.eat();
		System.out.println(p3.hashCode());
		System.out.println(p4.hashCode());
		
	
	}
}
從運行的結果可以看出來,作者只此一家別無分店,而作者的美國朋友有很多個。
作者還有個朋友zhangsan和lisi:

public class Zhangsan implements Person {

	public void init(){
		System.out.println("Zhangsan帶禮物去看朋友");
	}
	
	@Override
	public void eat() {
		System.out.println("Zhangsan吃了一碗飯");
	}
	
	public void destroy(){
		System.out.println("Zhangsan聊的很開心");
	}

}
public class Lisi implements Person {

	public void init(){
		System.out.println("Lisi帶禮物去看朋友");
	}
	
	@Override
	public void eat() {
		System.out.println("Lisi喝了二兩白酒");
	}
	
	public void destroy(){
		System.out.println("Lisi喝多了");
	}
}



spring-bean.xml的beans裏面增加:
<bean id="zhangsan" class="com.julyday.myspring.Zhangsan" init-method="init" destroy-method="destroy"></bean> 
<bean id="lisi" class="com.julyday.myspring.Lisi" init-method="init" destroy-method="destroy" lazy-init="true"></bean> 
測試下:
LazyTest.java
public class LazyTest extends BaseTest {
	@Test
	public void testBean(){
//		Zhangsan zhangsan = (Zhangsan)context.getBean("zhangsan");
//		zhangsan.eat();
		Lisi lisi = (Lisi)context.getBean("lisi");
		lisi.eat();
	}
}	
張三一開始沒來,我們運行下,看到好像spring讓張三來了,不科學啊!但仔細一看張三沒eat 啊,這裏就要講下spring管理bean的加載機制了,默認的是在容器啓動的時候spring把所有的單例的bean都創建出來的,張三單例,所以spring就調用了他的初始化方法init,當容器關閉的時候又調用了他的destroy方法。
如果是prototype類型的會這樣嗎?小夥伴可以自己去嘗試下。

以上的簡單例子我們再用註解的方式來實現一次
首先寫一個spring-beanannotation.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" 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">

	<!-- 告訴spring容器要掃描的bean類的包在哪 -->
	<!-- spring會掃描所有的@Component,@Repository,@Service 和 @Controller -->
	<context:component-scan base-package="com.julyday.myspring.annotation"></context:component-scan>

</beans>
其他的類加入註解如下:(爲了便於比較,我們新建了一個包com.julyday.myspring.annotation)
@Component
@Scope("prototype")
public class American implements Person{
	
	@Override
	public void eat() {
		System.out.println("美國人喜歡吃牛肉");
	}
}
@Component
public class Author implements Person{

	@Override
	public void eat() {
		System.out.println("作者喜歡吃肉");
	}
	
}
@Component
public class Lisi implements Person{
	
	@PostConstruct
	public void init(){
		System.out.println("Lisi帶禮物去看朋友");
	}
	
	@Override
	public void eat() {
		System.out.println("Lisi喝了二兩白酒");
	}
	
	@PreDestroy
	public void destroy(){
		System.out.println("Lisi喝多了");
	}
}
@Lazy(false)
@Component
public class Zhangsan implements Person{
	
	@PostConstruct
	public void init(){
		System.out.println("Zhangsan帶禮物去看朋友");
	}
	
	@Override
	public void eat() {
		System.out.println("Zhangsan吃了一碗飯");
	}
	
	@PreDestroy
	public void destroy(){
		System.out.println("Zhangsan聊的很開心");
	}
}
好的,我們再把BaseTest.java裏面的before方法context初始化修改成:context = new ClassPathXmlApplicationContext("spring-beanannotation.xml");
再運行我們的測試,和剛纔一樣了。顯然有細心的小夥伴發現,博主你錯了!LazyTest這個測試類的結果不對,這個是因爲你的Lisi類的路徑不對,加上import com.julyday.myspring.annotation.Lisi;或者測試類也寫一份在com.julyday.myspring.annotation包下。

@Component:告訴spring他是一個bean。
@Scope:bean的作用域。
@PostConstruct:bean初始化方法。
@PreDestroy:bean的銷燬方法。
@Lazy:bean的延遲加載機制,默認是true。

三:面向切面編程aop

下面講三種aop的實現:
第一種:api方式
博主吃完飯要回去了,先建一個新的Person接口:(在aop包下)
package com.julyday.myspring.aop;

public interface Person {
	
	public void back(String destination);
	
}
一樣的博主
public class Author implements Person{

	@Override
	public void back(String destination) {
		System.out.println("作者準備"+destination);
	}
	
}

博主在回去前很有禮貌的和各個朋友再見,那就要在back之前告訴spring。
BeforeAdvice.java
public class BeforeAdvice implements MethodBeforeAdvice{

	@Override
	public void before(Method method, Object[] args, Object target)
			throws Throwable {
		System.out.println("各位朋友再見!");
		String argString = "";
		if(args.length > 0){
			for(Object obj : args){
				argString += obj.toString();
			}
		}
		System.out.println("BeforeAdvice before : method : "+method.getName() 
				+" args : "+argString+" target : "+target.getClass().getName());
	}

}

新建一個spring-aop-api.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"
    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="beforeAdvice" class="com.julyday.myspring.aop.BeforeAdvice"></bean>
	
	<bean id="author"  class="org.springframework.aop.framework.ProxyFactoryBean">
		<property name="target">
			<bean class="com.julyday.myspring.aop.Author"></bean>
		</property>
		<property name="proxyInterfaces">
			<value>com.julyday.myspring.aop.Person</value>
		</property>
		<property name="interceptorNames">
			<list>
				<value>beforeAdvice</value>
			</list>
		</property>
	</bean>

 </beans>
新建一個測試類
@RunWith(BlockJUnit4ClassRunner.class)
public class ApiTest extends BaseTest{
	@Test
	public void testApi(){
		Person p = (Person)context.getBean("author");
		p.back("回家");
	}
}
public class BaseTest {
	public ClassPathXmlApplicationContext context;
	
	@Before
	public void before() {
		try {
			context = new ClassPathXmlApplicationContext("spring-aop-api.xml");
			context.start();
		} catch (BeansException e) {
			e.printStackTrace();
		}
	}
	
	@After
	public void after() {
		context.destroy();
	}
}
在博主回家前,spring完成了博主禮貌的before方法。
博主正準備回家,同事小紅讓博主去如家打牌:
public class AroundAdvice implements MethodInterceptor{

	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		System.out.println("小紅讓博主去如家666房間鬥地主");
		Object obj = invocation.proceed();
		System.out.println("博主告訴小紅,晚上有事明天再約");
		return obj;
	}

}
當博主準備去打牌的時候,老婆打電話讓我回家陪她看電影:
修改author

public class Author implements Person{

	@Override
	public void back(String destination) {
		System.out.println("作者準備"+destination);
		throw new RuntimeException("老婆打電話讓我回家陪她看電影");
	}
	
}

public class ThrowExAdvice implements ThrowsAdvice{
	
	public void afterThrowing(Exception ex) throws Throwable {
		System.out.println("ThrowExAdvice afterThrowing 1"+ex.getMessage());
	}
	
	public void afterThrowing(Method method, Object[] args, Object target, Exception ex) throws Throwable {
		System.out.println("ThrowExAdvice afterThrowing 2 : " + ex.getMessage());
	}
}
總的來說博主今天還是很開心:
public class AfterAdvice implements AfterReturningAdvice {

	@Override
	public void afterReturning(Object returnValue, Method method,
			Object[] args, Object target) throws Throwable {
		System.out.println("博主結束快樂的一天的行程");
	}

}
總的spring-aop-api.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"
    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="beforeAdvice" class="com.julyday.myspring.aop.BeforeAdvice"></bean>
	<bean id="afterAdvice" class="com.julyday.myspring.aop.AfterAdvice"></bean>
	<bean id="aroundAdvice" class="com.julyday.myspring.aop.AroundAdvice"></bean>
	<bean id="throwExAdvice" class="com.julyday.myspring.aop.ThrowExAdvice"></bean>
	
	<bean id="targetPerson" class="com.julyday.myspring.aop.Author"></bean>
	
	<bean id="author"  class="org.springframework.aop.framework.ProxyFactoryBean">
		<property name="proxyInterfaces">
			<value>com.julyday.myspring.aop.Person</value>
		</property>
		<property name="target">
			<!-- <bean class="com.julyday.myspring.aop.Author"></bean> -->
			<ref bean="targetPerson"/>
		</property>
		<property name="interceptorNames">
			<list>
				<value>beforeAdvice</value>
				<value>afterAdvice</value>
				<value>aroundAdvice</value>
				<value>throwExAdvice</value>
			</list>
		</property>
	</bean>

 </beans>
運行下:
@RunWith(BlockJUnit4ClassRunner.class)
public class ApiTest extends BaseTest{
	@Test
	public void testApi(){
		Person p = (Person)context.getBean("author");
		p.back("去打牌");
	}
}
這裏需要注意的是,當程序走到了ThrowExAdvice後,afterAdvice是不會執行的,如果沒有異常的話,afterAdvice是會執行的。

api的方式介紹完成後,我們再來看看基於xml配置的方式:
首先pom.xml增加:
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-aspects</artifactId>
	</dependency>
新增一個切面類AuthorAspect,切面類裏面有各種不通方法:
package com.julyday.myspring.aop.schema;

import org.aspectj.lang.ProceedingJoinPoint;

public class AuthorAspect {

	public void before() {
		System.out.println("AuthorAspect before.");
	}

	public void afterReturning() {
		System.out.println("AuthorAspect afterReturning.");
	}

	public void afterThrowing() {
		System.out.println("AuthorAspect afterThrowing.");
	}

	public void after() {
		System.out.println("AuthorAspect after.");
	}

	public Object around(ProceedingJoinPoint pjp) {
		Object obj = null;
		try {
			System.out.println("AuthorAspect around 1.");
			obj = pjp.proceed();
			System.out.println("AuthorAspect around 2.");
		} catch (Throwable e) {
			e.printStackTrace();
		}
		return obj;
	}

}
新增spring-aop-schema-advice.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"
    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-4.0.xsd">

	<bean id="authorAspect" class="com.julyday.myspring.aop.schema.AuthorAspect"></bean>
	
	<bean id="author" class="com.julyday.myspring.aop.Author"></bean>
	
	<aop:config>
		<aop:aspect id="authorAspectAOP" ref="authorAspect">
 			<aop:pointcut expression="execution(* com.julyday.myspring.aop.Author.*(..))" id="authorPiontcut"/>
			<aop:before method="before" pointcut-ref="authorPiontcut"/>
			<aop:after-returning method="afterReturning" pointcut-ref="authorPiontcut"/>
			<aop:after-throwing method="afterThrowing" pointcut-ref="authorPiontcut"/>
			<aop:after method="after" pointcut-ref="authorPiontcut"/>
			<aop:around method="around" pointcut-ref="authorPiontcut"/>
		</aop:aspect>
	</aop:config>

 </beans>
兩個bean不多說了,在config裏面先定義一個切面,指向切面的bean,切面裏面定義一個切點pointcut,切點是告訴spring你從那個地方去切入,後面就是要切入的時機了。
這裏的expression="execution(* com.julyday.myspring.aop.Author.*(..))" 第一個* 表示的是所有的權限修飾符都匹配,類不說了,第二個*表示的是這個類的所有方法,括號裏面的..表示的是所有形式和個數的參數,當然除了execution還有其他的,可以參考spring官網裏面的,這裏就不再深入了。


接着測試下:
package com.julyday.myspring.aop.schema;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;

import com.julyday.myspring.aop.Person;

@RunWith(BlockJUnit4ClassRunner.class)
public class SchemaTest extends BaseTest{
	@Test
	public void testApi(){
		Person p = (Person)context.getBean("author");
		p.back("去打牌");
	}
}
package com.julyday.myspring.aop.schema;

import org.junit.After;
import org.junit.Before;
import org.springframework.beans.BeansException;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BaseTest {
	public ClassPathXmlApplicationContext context;
	
	@Before
	public void before() {
		try {
			context = new ClassPathXmlApplicationContext("spring-aop-schema-advice.xml");
			context.start();
		} catch (BeansException e) {
			e.printStackTrace();
		}
	}
	
	@After
	public void after() {
		context.destroy();
	}
}
這裏需要指出的是如果出現異常,後面的after 和afterReturning是會繼續執行的,和api的有點不一樣。

第三種:註解的方式。

package com.julyday.myspring.aop.aspectj;

import org.springframework.stereotype.Component;
import com.julyday.myspring.aop.Person;

@Component
public class Author implements Person {

	@Override
	public void back(String destination) {
		System.out.println("作者準備"+destination);
		throw new RuntimeException("老婆打電話讓我回家陪她看電影");
	}

}

package com.julyday.myspring.aop.aspectj;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class AuthorAspect {
	
	@Pointcut("execution(* com.julyday.myspring.aop.aspectj.Author.*(..))")
	public void pointcut(){}
	
	@Before("execution(* com.julyday.myspring.aop.aspectj.Author.*(..))")
	public void before() {
		System.out.println("AuthorAspect before.");
	}
	
	@AfterReturning(value="pointcut()")
	public void afterReturning() {
		System.out.println("AuthorAspect afterReturning.");
	}
	
	@AfterThrowing("pointcut()")
	public void afterThrowing() {
		System.out.println("AuthorAspect afterThrowing.");
	}
	
	@After("pointcut()")
	public void after() {
		System.out.println("AuthorAspect after.");
	}

	@Around("pointcut()")
	public Object around(ProceedingJoinPoint pjp) {
		Object obj = null;
		try {
			System.out.println("AuthorAspect around 1.");
			obj = pjp.proceed();
			System.out.println("AuthorAspect around 2.");
		} catch (Throwable e) {
			e.printStackTrace();
		}
		return obj;
	}
}
spring-aop-aspectj.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"
    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="com.julyday.myspring.aop.aspectj"></context:component-scan>
     	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

 </beans>
測試下:
package com.julyday.myspring.aop.aspectj;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;

import com.julyday.myspring.aop.Person;

@RunWith(BlockJUnit4ClassRunner.class)
public class AspectjTest extends BaseTest{
	@Test
	public void aspectjApi(){
		Person p = (Person)context.getBean("author");
		p.back("去打牌");
	}
}
package com.julyday.myspring.aop.aspectj;

import org.junit.After;
import org.junit.Before;
import org.springframework.beans.BeansException;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BaseTest {
	public ClassPathXmlApplicationContext context;
	
	@Before
	public void before() {
		try {
			context = new ClassPathXmlApplicationContext("spring-aop-aspectj.xml");
			context.start();
		} catch (BeansException e) {
			e.printStackTrace();
		}
	}
	
	@After
	public void after() {
		context.destroy();
	}
}
這種方式和xml配置的方式基本差不多。
好的,spring的簡單入門就講到這裏了。

最後放上項目所有的代碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章