Spring框架是一款运用十分广阔的常见的java开源框架。那么对于一个程序员来说,如果没有接触过这这框架,我们应该如何入手呢,我觉得要搞清楚Spring框架首先要搞清楚三个概念,然后由浅入深逐渐学习。
这三个概念就是:
1.控制反转(IOC),
2.依赖注入(DI),
3.面向切面编程(AOP),
一、控制反转
要搞清楚控制反转首先要了解容器的概念,那么什么是容器呢。通俗来说容器就是用来创造对象的。所谓控制反转就是指把创造对象的过程交给Spring容器,那么是怎么交给Spring容器的呢。这里就要用依赖注入的方式来把我们需要创造对象的过程交给Spring来处理,所以来说依赖注入(DI)是Spring控制反转(DI)的一种方式。
二、依赖注入
前面说了依赖注入是Spring控制反转的一种方式那么是通过什么方式来创建对象的呢。总的来说Spring提供了两种方法来让我们很方便的创造对象:一是通过在Spring核心配置文件 applicationContext.xml(一般建议使用这个名称);二是通过一系列的注解(@Autowired 、@Resource、@Component、@Constroller、@Service、@Repository)这其中@Autowired 和@Resource依然需要在配置文件中配置<bean>。
我们可以先用一个用核心配置文件applicationContext.xml的方式做一个Spring的入门。
1、配置文件方式
1.1Spring入门
1> 导入Spring相关包
2> 编写配置文件(1.编写配置文件的头 2编写配置文件的实体内容)
<?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-3.2.xsd ">
<!--
id="唯一标识这个bean" id绝对不能重复
id的命名规则:首字母小写其他驼峰规则 PersonService
class就是对象的具体路径
-->
<bean id="hello" class="bean.Hello"></bean>
</beans>
其中,bean标签中id是后边class的唯一标识符,class是生成对象的类的路径。
3> 启动Spring容器
4> .从容器中获取对象,调用方法完成相应的功能
首先要启动Spring容器,就是传入Spring核心配置文件。
创建对象原理:Spring会运行时会逐行扫面配置文件,当碰到<bean>时会根据class属性,通过java的反射机制来创建对象,并放入Spring的容器中。
获取对象原理:1(byName):通过bean的id,context.getBean("hello"); 2(byType):通过class类型获取对象,context.getBean(Hello.class);
1.2依赖注入的三种方式
1> 接口注入
Spring核心配置文件
根据接口获得实现类对象.
2>构造方法注入
通过index和name都能为对象的属性赋值,但是一般推荐使用index。这样可以保证在没有导入源码的情况下赋值正确。
3> set方法注入
注意:使用set方法注入时,class必须要有set方法,否则无效。
bean中还有许多属性比如:Parent属性用来维护子父级关系;scopee用来控制单例和双例
2、添加注解方式
2.1@Autowired注解
@Autowired用来完成自动装配工作(就是创建对象),可以对类的成员变量,方法,及构造函数进行标注。
1> 标注成员变量
比如boss有office和car两个属性
Spring 通过一个 BeanPostProcessor 对 @Autowired 进行解析,
所以要让@Autowired 起作用必须事先在 Spring 容器中声明 AutowiredAnnotationBeanPostProcessor Bean。
这样,当Spring 容器启动时,AutowiredAnnotationBeanPostProcessor 将扫描 Spring 容器中所有 Bean,
当发现 Bean 中拥有@Autowired 注释时就找到和其匹配(默认按类型匹配)的 Bean,并注入到对应的地方中去。
按照上面的配置,Spring将直接采用 Java 反射机制对 Boss 中的 car 和 office 这两个私有成员变量进行自动注入。
所以对成员变量使用@Autowired 后,您大可将它们的 setter 方法(setCar() 和 setOffice())从 Boss 中删除。
2> 标注方法
这时,@Autowired 将查找被标注的方法的入参类型的 Bean,并调用方法自动注入这些 Bean。
3> 标注构造函数
由于 Boss() 构造函数有两个入参,分别是 car 和 office,@Autowired 将分别寻找和它们类型匹配的 Bean,
将它们作为Boss(Car car ,Office office) 的入参来创建 Boss Bean。
@Autowired(required = false)
2.2 @Qualifier
2.3 @Resource
@Resource和 @Autowired 的作用一样都是直接注入其他对象的作用,
@Resource有两个属性一个是name一个是type,name就是byname自动注入策略,而type就是bytypez注入策略。
2.4 @PostConstruct和@PreDestroy
@PostConstruct和@PreDestroy是初始化后注释/销毁前注释,只能用在方法中。
2.5 使用<context:annotation-config/>简化配置文件
<context:annotation-config/> 将隐式地向 Spring 容器注册AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、
PersistenceAnnotationBeanPostProcessor以及equiredAnnotationBeanPostProcessor 这 4 个BeanPostProcessor,
我们知道注释本身并不起作用,它们只是元数据,而真正起作用的是通过元数据相应的处理器,就像前面所提到的4个BeanPostProcessor。
在配置文件中使用 context 命名空间之前,必须在 <beans> 元素中声明 context 命名空间。
2.6 @Component、@Constroller 、@Service、@Repository
虽然我们可以通过 @Autowired 或 @Resource 在 Bean 类中使用自动注入功能,但是 Bean 还是在 XML 文件中通过<bean> 进行定义
—— 也就是说,在 XML 配置文件中定义 Bean,通过@Autowired 或 @Resource为 Bean 的成员变量、方法入参或构造函数入参提供
自动注入的功能。能否也通过注释定义 Bean,从 XML 配置文件中完全移除 Bean 定义的配置呢?答案是肯定的,我们通过 Spring 2.5
提供的@Component 注释就可以达到这个目标了。
为什么@Repository只能标注在dao类上呢?
这是因为该注解的作用不止是将类标识为bean,同时她还可以把所标注的类中抛出的数据访问异常封装为Spring的数据访问异常类型,
Spring 本身提供了一个丰富的并且是与具体的数据访问技术无关的数据访问异常结构,用于封装不同的持久层框架抛出的异常,使得异常
独立于底层的框架。
Spring 2.5 在 @Repository 的基础上增加了功能类似的额外三个注解:@Component、@Service、@Constroller,它们分别用于软件系统的
不同层次:
• @Component 是一个泛化的概念,仅仅表示一个组件 (Bean) ,可以作用在任何层次。 • @Service 通常作用在业务层,但是目前该功能与 @Component 相同。 • @Constroller 通常作用在控制层,但是目前该功能与 @Component 相同。 通过在类上使用 @Repository、@Component、@Service 和 @Constroller 注解,Spring 会自动创建相应的 BeanDefinition 对象,并注册到ApplicationContext 中。这些类就成了 Spring 受管组件。这三个注解除了作用于不同软件层次的类,其使用方式与 @Repository 是完全相同的。
2.7 开启包扫描
过滤器类型 | 说明 |
注释 | 假如 com.baobaotao.SomeAnnotation 是一个注释类,我们可以将使用该注释的类过滤出来。 |
类名指定 | 通过全限定类名进行过滤,如您可以指定将 com.baobaotao.Boss 纳入扫描,而将 com.baobaotao.Car 排除在外。 |
正则表达式 | 通过正则表达式定义过滤的类,如下所示: com\.baobaotao\.Default.* |
AspectJ 表达式 | 通过 AspectJ 表达式定义过滤的类,如下所示: com. baobaotao..*Service+ |
下面是一个简单的例子:
值得注意的是 <context:component-scan/> 配置项不但启用了对类包进行扫描以实施注释驱动 Bean 定义的功能,
同时还启用了注释驱动自动注入的功能(即还隐式地在内部注册了AutowiredAnnotationBeanPostProcessor 和
CommonAnnotationBeanPostProcessor),因此当使用 <context:component-scan/> 后,
就可以将<context:annotation-config/> 移除了。
三、Spring AOP
AOP,即Aspect orientied program,就是面向方面(切面)的编程。
功能: 让关注点代码与业务代码分离!
关注点:重复代码就叫做关注点。
业务代码:核心业务代码。
业务代码和关注点代码分离的好处:
--> 关注点代码写一次即可;
-->开发者只需要关注核心业务;
-->运行时期,执行核心业务代码时候动态植入关注点代码;【代理】
切面:
关注点形成的类,就叫切面(类)!
面向切面编程,就是指 对很多功能都有的重复的代码抽取,再在运行的时候往业务方法上动态植入“切面类代码”。
切入点(拦截的作用):
执行目标对象方法,动态植入切面代码。
可以通过切入点表达式,指定拦截哪些类的哪些方法; 给指定的类在运行的时候植入切面类代码。
1、AOP配置文件方式实现
1.1导入相关jar包
先引入aop相关jar文件 (aspectj aop优秀组件)
spring-aop-3.2.5.RELEASE.jar 【spring3.2源码】
aopalliance.jar 【spring2.5源码/lib/aopalliance】
aspectjweaver.jar 【spring2.5源码/lib/aspectj】或【aspectj-1.8.2\lib】
aspectjrt.jar 【spring2.5源码/lib/aspectj】或【aspectj-1.8.2\lib】
1.2bean.xml中引入aop名称空间
1.3定义接口及实现类
定义接口
public interface HelloWorld
{
void printHelloWorld();
void doPrint();
}
定义两个实现类
public class HelloWorldImpl1 implements HelloWorld
{
public void printHelloWorld()
{
System.out.println("Enter HelloWorldImpl1.printHelloWorld()");
}
public void doPrint()
{
System.out.println("Enter HelloWorldImpl1.doPrint()");
return ;
}
}
public class HelloWorldImpl2 implements HelloWorld
{
public void printHelloWorld()
{
System.out.println("Enter HelloWorldImpl2.printHelloWorld()");
}
public void doPrint()
{
System.out.println("Enter HelloWorldImpl2.doPrint()");
return ;
}
}
1.4定义切面
public class TimeHandler
{
public void printTime()
{
System.out.println("CurrentTime = " + System.currentTimeMillis());
}
}
1.5编写配置文件
<?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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
<bean id="helloWorldImpl1" class="com.xrq.aop.HelloWorldImpl1" />
<bean id="helloWorldImpl2" class="com.xrq.aop.HelloWorldImpl2" />
<bean id="timeHandler" class="com.xrq.aop.TimeHandler" />
<aop:config>
<aop:aspect id="time" ref="timeHandler">
<aop:pointcut id="addAllMethod" expression="execution(* com.xrq.aop.HelloWorld.*(..))" />
<aop:before method="printTime" pointcut-ref="addAllMethod" />
<aop:after method="printTime" pointcut-ref="addAllMethod" />
</aop:aspect>
</aop:config>
</beans>
<!-- 配置文件说明 -->
<!-- 先把类交给Spring容器 -->
<bean id="helloWorldImpl1" class="com.xrq.aop.HelloWorldImpl1" />
<bean id="helloWorldImpl2" class="com.xrq.aop.HelloWorldImpl2" />
<bean id="timeHandler" class="com.xrq.aop.TimeHandler" />
<!-- 包括两个实现类和一个切面类。-->
<aop:config>
<aop:aspect id="time" ref="timeHandler">
<!-- Aspect定义切面类 -->
<aop:pointcut id="addAllMethod" expression="execution(* com.xrq.aop.HelloWorld.*(..))" />
<!-- Pointcut定义切入点,excution属性下的包路径名,表示这个路径下的类或者方法有切面类插入。 -->
<aop:before method="printTime" pointcut-ref="addAllMethod" />
<!-- 前置通知 -->
<aop:after method="printTime" pointcut-ref="addAllMethod" />
<!-- 后置通知 -->
</aop:aspect>
</aop:config>
1.6测试
public static void main(String[] args)
{
ApplicationContext ctx =
new ClassPathXmlApplicationContext("aop.xml");
HelloWorld hw1 = (HelloWorld)ctx.getBean("helloWorldImpl1");
HelloWorld hw2 = (HelloWorld)ctx.getBean("helloWorldImpl2");
hw1.printHelloWorld();
System.out.println();
hw1.doPrint();
System.out.println();
hw2.printHelloWorld();
System.out.println();
hw2.doPrint();
}
测试结果
注意
当有多个切面类时:
可以用以下方法来规定执行顺序
(1)aspect里面有一个order属性,order属性的数字就是横切关注点的顺序 (2)把logHandler定义在timeHandler前面,Spring默认以aspect的定义顺序作为织入顺序2、AOP注解方式实现
2.1导入相关包
aspectjrt.jar,
aspectjweaver.jar,
cglib-nodep.jar.
2.2定义接口及实现类
接口
package com.bird.service;
public interface PersonServer {
public void save(String name);
public void update(String name, Integer id);
public String getPersonName(Integer id);
}
实现类
package com.bird.service.impl;
import com.bird.service.PersonServer;
public class PersonServiceBean implements PersonServer{
@Override
public void save(String name) {
System.out.println("我是save方法");
// throw new RuntimeException();
}
@Override
public void update(String name, Integer id) {
System.out.println("我是update()方法");
}
@Override
public String getPersonName(Integer id) {
System.out.println("我是getPersonName()方法");
return "xxx";
}
}
2.3定义切面类
package com.bird.service;
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;
/**
* 切面
* @author Bird
*
*/
@Aspect
public class MyInterceptor {
@Pointcut("execution(* com.bird.service.impl.PersonServiceBean.*(..))")
private void anyMethod(){}//定义一个切入点
@Before("anyMethod() && args(name)")
public void doAccessCheck(String name){
System.out.println(name);
System.out.println("前置通知");
}
@AfterReturning("anyMethod()")
public void doAfter(){
System.out.println("后置通知");
}
@After("anyMethod()")
public void after(){
System.out.println("最终通知");
}
@AfterThrowing("anyMethod()")
public void doAfterThrow(){
System.out.println("例外通知");
}
@Around("anyMethod()")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("进入环绕通知");
Object object = pjp.proceed();//执行该方法
System.out.println("退出方法");
return object;
}
}
@Pointcut("execution(* com.bird.service.impl.PersonServiceBean.*(..))")
2.4编写配置文件,打开aop命名空间
<?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-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<aop:aspectj-autoproxy/>
<bean id="personServiceBean" class="com.bird.service.impl.PersonServiceBean"/>
<bean id="myInterceptor" class="com.bird.service.MyInterceptor"/>
</beans>
2.5测试
package junit.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.bird.service.PersonServer;
public class SpringAOPTest {
@Test
public void inteceptorTest(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("beanAop.xml");
PersonServer bean = (PersonServer)ctx.getBean("personServiceBean");
bean.save(null);
}
测试结果
1. 2012-3-12 18:08:39 org.springframework.context.support.AbstractApplicationContext prepareRefresh
2. 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@dd20f6:
display name [org.springframework.context.support.ClassPathXmlApplicationContext@dd20f6];
startup date [Mon Mar 12 18:08:39 CST 2012]; root of context hierarchy
3. 2012-3-12 18:08:40 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 4. 信息: Loading XML bean definitions from class path resource [beanAop.xml] 5. 2012-3-12 18:08:40 org.springframework.context.support.AbstractApplicationContext obtainFreshBeanFactory 6. 信息: Bean factory for application context [org.springframework.context.support.ClassPathXmlApplicationContext@dd20f6]:org.springframework.beans.factory.support.DefaultListableBeanFactory@b0bad7 7. 2012-3-12 18:08:40 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons 8. 信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@b0bad7:
defining beans [org.springframework.aop.config.internalAutoProxyCreator,personServiceBean,myInterceptor]; root of factory hierarchy 9. null 10. 前置通知 11. 进入环绕通知 12. 我是save方法 13. 后置通知 14. 退出方法 15. 最终通知