目前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
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>
測試下: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();
}
}
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("美國人喜歡吃牛肉");
}
}
@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());
}
}
從運行的結果可以看出來,作者只此一家別無分店,而作者的美國朋友有很多個。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喝多了");
}
}
<bean id="lisi" class="com.julyday.myspring.Lisi" init-method="init" destroy-method="destroy" lazy-init="true"></bean>
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方法。<?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");三:面向切面編程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。
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());
}
}
<?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;
}
}
當博主準備去打牌的時候,老婆打電話讓我回家陪她看電影: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配置的方式:
<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你從那個地方去切入,後面就是要切入的時機了。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配置的方式基本差不多。