Spring 学习
文章目录
仓库地址: https://github.com/biuaxia/spring
Spring
IOC/DI
Spring是一个基于IOC和AOP的结构J2EE系统的框架
IOC 反转控制 是Spring的基础,Inversion Of Control
简单说就是创建对象由以前的程序员自己new 构造方法来调用,变成了交由Spring创建对象
DI 依赖注入 Dependency Inject. 简单地说就是拿到的对象的属性,已经被注入好相关值了,直接使用即可。
必读: 基于框架的程序要成功运行,对于JAR包的版本,配置文件的正确性有着苛刻的要求,任何一个地方出错了,都会导致框架程序运行失败。 如果你是第一次学习本框架,务必严格按照教程的指导,完全模仿操作,直到成功看到运行效果。 第一次成功之后,信心,思路都会有较好的铺垫,然后再根据自己的疑惑,在“成功”的代码上做原本想做的改动和调整,这样可以大大节约学习的时间,提高效率,切勿一来就擅自改动,给自己的学习制造障碍
建立普通的Java项目
导入jar包
建立pojo Category用于演示IOC/DI
package vip.javer.pojo;
/**
* @author Administrator
*/
public class Category {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
applicationContext.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"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean name="c" class="vip.javer.pojo.Category">
<property name="name" value="category 1"/>
</bean>
</beans>
TestSpring
测试代码,演示通过spring获取Category对象,以及该对象被注入的name属性。
总结和原理解释
总结
到目前为止,简单的使用就是
- 对应的实体类
- 在applicationContext.xml中配置对应的bean,并在bean中写入对应的property属性name和value
- 需要使用的地方采取
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml")
得到的app对象getBean即可
IOC就是交由Spring管理对象
DI就是依赖注入,简单说就是拿到对象属性
原理解释
以获取对象的方式进行比较
-
传统方式
通过new关键字主动创建一个对象 -
IOC方式
对象的生命周期由Spring进行管理,直接去Spring那里获取一个对象,IOC是反转控制的缩写,就好比控制权原来在自己手里,现在转给了Spring
打个比喻:
- 传统方式:相当于你自己去菜市场new 了一只鸡,不过是生鸡,要自己拔毛,去内脏,再上花椒,酱油,烤制,经过各种工序之后,才可以食用。
- 用IOC:相当于去馆子(Spring)点了一只鸡,交到你手上的时候,已经五味俱全,你就只管吃就行了。
练习获取一个Product对象
- 对应的实体类
- xml追加bean配置
<bean name="p" class="vip.javer.pojo.Product">
<property name="id" value="1"/>
<property name="name" value="产品1"/>
</bean>
- 在测试类中获取即可
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
Product p = app.getBean("p",Product.class);
System.out.println(p.getId() + "\t" + p.getName());
注入对象
在上例中,对Category的name属性注入了"category 1"字符串
在本例中 ,对Product对象,注入一个Category对象
- xml追加bean定义
<bean name="c" class="vip.javer.pojo.Category">
<property name="id" value="1"/>
<property name="name" value="分类1"/>
</bean>
<bean name="p1" class="vip.javer.pojo.Product">
<property name="id" value="2"/>
<property name="name" value="产品2"/>
<property name="category" ref="c"/>
</bean>
- 测试类测试
@Test
public void injectedObject() {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
Product p = app.getBean("p1", Product.class);
System.out.println(p);
}
注解方式实现IOC/DI
在本知识点,会演示如何使用注解的方式完成注入对象中的效果
-
xml添加
<context:annotation-config/>
表示告诉Spring要使用注解的方式进行配置 -
干掉之前的注入
<property>
,我的实际代码只是干掉了一行(随便哪行都行,方便后面进行注解完成该操作)<?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" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <!--开启注解--> <context:annotation-config/> <bean name="c" class="vip.javer.pojo.Category"> <property name="name" value="category 1"/> <property name="id" value="233"/> </bean> <bean name="p" class="vip.javer.pojo.Product"> <property name="id" value="1"/> <property name="name" value="产品"/> </bean> <bean name="p1" class="vip.javer.pojo.Product"> <property name="id" value="2"/> <property name="name" value="产品2"/> <!--<property name="category" ref="c"/>--> </bean> </beans>
-
在Product的category属性前加上注解
@Autowired
package vip.javer.pojo; import org.springframework.beans.factory.annotation.Autowired; /** * @author Administrator */ public class Product { int id; String name; @Autowired Category category; @Override public String toString() { return "Product{" + "id=" + id + ", name='" + name + '\'' + ", category=" + category + '}'; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Category getCategory() { return category; } public void setCategory(Category category) { this.category = category; } }
-
测试
@Test public void injectedObject() { ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); Product p = app.getBean("p1", Product.class); System.out.println(p); }
@Autowired的位置
除了在属性前加上@Autowired,还可以在setCategory方法前加上@Autowired,是相同的效果
除了@Autowired还有@Resource
@Resource(name = "c")
Category category;
怎么注入bean(缩减xml配置)
上述例子是对注入对象行为的注解,那么bean对象本身,比如Category,Product可不可以移出applicationContext.xml配置文件,也通过注解进行呢?
接下来就讲解如何对Bean进行注解配置
applicationContext.xml
修改applicationContext.xml,什么都去掉,只新增一行代码
<context:component-scan base-package="vip.javer.pojo"/>
该代码的作用是告诉Spring,bean都放在了vip.javer.pojo这个包下
@Component
- 分别为Product和Category类加上@Component,表明该类是bean,写法@Component(“c”)或者@Component(value = “p”)都可以
- 因为配置从xml移出来了,所以属性初始化放在属性声明上进行了
运行可以看到和之前的效果一样,之前的p1由于@Component声明的p,自然失败了
AOP
AOP 即 Aspect Oriented Program 面向切面编程
首先,在面向切面编程的思想里面,把功能分为核心业务功能 ,和周边功能。
所谓的核心业务,比如登陆,增加数据,删除数据都叫核心业务
所谓的周边功能,比如性能统计,日志,事务管理等等
周边功能在Spring的面向切面编程AOP思想里,即被定义为切面
在面向切面编程AOP的思想里面,核心业务功能和切面功能分别独立进行开发
然后把切面功能和核心业务功能 “编织” 在一起,这就叫AOP
例如现在我也业务核心代码是sout,我想对核心代码切入日志功能
-
准备业务类
package vip.javer.service; /** * @author Administrator */ public class ProductService { public void doSomeService() { System.out.println("doSomeService"); } }
-
准备日志切面LoggerAspect
package vip.javer.aspect; import org.aspectj.lang.ProceedingJoinPoint; /** * @author Administrator */ public class LoggerAspect { public Object log(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("start log:\t" + joinPoint.getSignature().getName()); Object object = joinPoint.proceed(); System.out.println("eng log:\t" + joinPoint.getSignature().getName()); return object; } }
需要解释一下的是,该日志切面的功能是在调用核心功能之前和之后分别打印日志
Object object = joinPoint.proceed();
就是将来与某个核心功能编制之后,用于执行核心功能的代码 -
applicationContext.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" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <!--声明业务对象--> <bean name="s" class="vip.javer.service.ProductService"/> <!--声明日志切面--> <bean id="log" class="vip.javer.aspect.LoggerAspect"/> <!--4. 里面的核心业务和辅助功能写好后,通过aop:config把业务对象与辅助功能编制在一起--> <aop:config> <!--1. 指定核心业务功能 登录,查询,生成订单--> <aop:pointcut id="loggerCutpoint" expression="execution(* vip.javer.service.ProductService.*(..))"/> <!--2. 指定辅助功能 性能统计,日志输出,事务管理--> <aop:aspect id="logAspect" ref="log"> <!--3. 切入点为loggerCuppoint,切入方法为log对象的log方法,切入方式为前后切入--> <aop:around method="log" pointcut-ref="loggerCutpoint"/> </aop:aspect> </aop:config> </beans>
按照注释的顺序查看
解释一下
execution(* vip.javer.service.ProductService.*(..))
,这表示对满足如下条件的方法调用,进行切面操作*
返回任意类型vip.javer.service.ProductService.*
以vip.javer.service.ProductService
开头的类的任意方法(..)
参数是任意数量和类型 -
测试
TestSpring 代码没有发生任何变化,通过配置的方式,把切面和核心业务类编制在了一起。
运行测试,可以发现在编织之后,业务方法运行之前和之后分别会打印日志
练习-性能统计切面
参考上述的日志切面,做一个性能统计切面,并编织在业务方法上面。
注: 在业务方法方法中,做一些JDBC访问,以增加耗时
答案
-
由于已有主业务类ProductService,故略过
-
性能统计切面PerformanceStatisticsAspect
package vip.javer.aspect; import org.aspectj.lang.ProceedingJoinPoint; /** * @author Administrator */ public class PerformanceStatisticsAspect { public Object statistics(ProceedingJoinPoint joinPoint) throws Throwable { long startTime = System.currentTimeMillis(); Object object = joinPoint.proceed(); Thread.sleep(500); long endTime = System.currentTimeMillis(); long runningTime = endTime - startTime; System.out.println("运行时间:\t[" + runningTime + "ms]"); return object; } }
-
配置applicationContext.xml
<!--性能统计切面--> <bean name="per" class="vip.javer.aspect.PerformanceStatisticsAspect"/> <!--aop配置--> <aop:config> <aop:pointcut id="ps" expression="execution(* vip.javer.service.ProductService.*(..))"/> <aop:aspect id="statistics" ref="per"> <!--环绕通知--> <aop:around method="statistics" pointcut-ref="ps"/> </aop:aspect> </aop:config>
-
测试
@Test public void aopCutIntoTheAnswer() { ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); ProductService s = app.getBean("s", ProductService.class); s.doSomeService(); }
补充一下aop:config
配置的一些说明
注解AOP
注解很简单,基于上面的例子
- 为ProductService添加@Component(“s”)标记
- 去除application.xml中的无用和重复配置
- application.xml添加三行代码(分别是两行<context:component-scan base-package=“切面,service包”/>,以及一行aop的自动配置
<aop:aspectj-autoproxy/>
) - 测试即可
补充一点
不同切面类可通过实现org.springframework.core.Ordered 接口实现切面类的优先级控制,具体为重写getOrder方法,定制返回值,返回值(int 类型)越小优先级越大
例如我想让性能监控最先
package vip.javer.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
/**
* @author Administrator
*/
@Aspect
@Component
public class PerformanceStatisticsAspect implements Ordered {
@Around("execution(* vip.javer.service.ProductService.*(..))")
public Object statistics(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("测试内容");
long startTime = System.currentTimeMillis();
Object object = joinPoint.proceed();
Thread.sleep(500);
long endTime = System.currentTimeMillis();
long runningTime = endTime - startTime;
System.out.println("运行时间:\t[" + runningTime + "ms]");
return object;
}
@Override
public int getOrder() {
return 1;
}
}
注解测试
注解方式用到了junit,所以需要下载:
junit-4.12.jar和hamcrest-all-1.3.jar
-
导包
-
修改TestSpring
修改TestSpring, 并运行
-
@RunWith(SpringJUnit4ClassRunner.class)
表示这是一个Spring的测试类 -
@ContextConfiguration(“classpath:applicationContext.xml”)
定位Spring的配置文件 -
@Autowired
给这个测试类装配Category对象 -
@Test
测试逻辑,打印c对象的名称
package vip.javer.test; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import vip.javer.pojo.Category; import vip.javer.pojo.Product; import vip.javer.service.ProductService; /** * @author Administrator */ /*1. 表明这是Spring的测试类*/ @RunWith(SpringJUnit4ClassRunner.class) /*2. 定位Spring的配置文件*/ @ContextConfiguration("classpath:applicationContext.xml") public class TestSpring { /*3. 给测试类装配Category对象*/ @Autowired private Category c; /*4. 测试逻辑,打印c的名称*/ @Test public void annotationTest() { System.out.println(c.getName()); } @Test public void gettingObjectsUsingSpring() { // ApplicationContext app = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"}); ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); Category c = (Category) app.getBean("c"); System.out.println(c.getId() + "\t" + c.getName()); Product p = app.getBean("p", Product.class); System.out.println(p.getId() + "\t" + p.getName()); } @Test public void annotationMode() { ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); Product p = app.getBean("p", Product.class); System.out.println(p); } @Test public void aopTest() { ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); ProductService ps = app.getBean("s", ProductService.class); ps.doSomeService(); } }
-