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的简单入门就讲到这里了。

最后放上项目所有的代码

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