spring入门及常用注解的使用

目前java主流的开源框架无论是ssh还是ssi,除了spring,其他的框架都有可替换的框架struts2和springmvc,hibernate和ibatis(mybatis),这里我们不讨论其他的框架的优劣。那为什么spring这么受开发者的欢迎呢?那就是spring的优点,博主以为spring的优点有:

1.轻量级:你可以选择你想要的服务,还有代码的低侵入性。

2.控制反转ioc:Spring使用控制反转技术实现了松耦合。依赖被注入到对象,而不是创建或寻找依赖对象。

3.面向切面编程aop:Spring支持面向切面编程,同时把应用的业务逻辑与系统的服务分离开来。

4.事务管理:spring有着强大的事务处理能力。

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

那下面我们结合着代码依次来说说spring的各个有点

一:轻量级


	4.0.0

	com.julyday
	myspring
	0.0.1-SNAPSHOT
	jar

	myspring
	http://maven.apache.org

	
		UTF-8
		4.1.4.RELEASE
	

	
		
			junit
			junit
			4.12
			test
		

		
		
			org.springframework
			spring-core
		

		
			org.springframework
			spring-beans
		

		
			org.springframework
			spring-context
		
	
	
		
			
				org.springframework
				spring-framework-bom
				${spring.version}
				pom
				import
			
		
	


上面是我们需要用到的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,如下:


        
        
     
测试下:
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里面增加:


这里加了一个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里面增加:
 
 
测试下:
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



	
	
	

其他的类加入注解如下:(为了便于比较,我们新建了一个包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:


        
	
	
	
		
			
		
		
			com.julyday.myspring.aop.Person
		
		
			
				beforeAdvice
			
		
	

 
新建一个测试类
@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修改如下:


        
	
	
	
	
	
	
	
	
		
			com.julyday.myspring.aop.Person
		
		
			
			
		
		
			
				beforeAdvice
				afterAdvice
				aroundAdvice
				throwExAdvice
			
		
	

 
运行下:
@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增加:
	
		org.springframework
		spring-aspects
	
新增一个切面类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文件:



	
	
	
	
	
		
 			
			
			
			
			
			
		
	

 
两个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:


        
        
     	

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

最后放上项目所有的代码
https://github.com/Julyday/myspring.git

你可能感兴趣的:(spring)