第一章:Spring简介
第一节:Spring简介
第二节:七大模块
第二章:Ioc
第一节:项目创建
第二节:XML方式注入bean
第三节:注解形式注入bean
第三章:Aop
第四章:SSM整合
第一章:Spring简介
第一节:Spring简介
- Spring是一个开源的控制反转(Inversion of Control,IoC)和面向切面(AOP)的容器框架,它的主要目得是简化企业开发。
- 使用Spring来完成bean的构建,使得其他应用层只专注于自己的业务逻辑处理。
- Spring框架通过依赖注入DI(Dependency Injection)或者控制反转IoC的方式来管理各个对象 。
l IoC : 控制反转就是应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护是由外部容器负责的。这样控制权就由应用转移到了外部容器,控制权的转移就是所谓反转。
l DI : 在运行期,由外部容器动态地将依赖对象注入到组件中。
- 特点:轻量级、低侵入式
- 核心容器Core:负责Bean的管理和构建,底层使用BeanFactory工厂模式来实现。BeanFactory使用依赖注入的方式提供给组件依赖。
- Spring的上下文Context:国际化等,Spring上下文是一个配置文件,主要向框架提供上下文信息。
- Spring AOP:面向切面的编程
- Spring Dao:它主要和dao层相关联,可以用该结构来管理异常处理和不同数据库供应商抛出的错误信息。其中异常层次结构简化了错误处理,并且极大地降低了需要编写地异常代码数据(例如打开和关闭连接)。
- Spring ORM:实体关系映射
- Spring Web:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
- Spring Web MVC
第二节:七大模块
第二章:Ioc
第一节:项目创建
- 创建Maven项目,不勾选任何模板
- pom.xml
|
<properties>
<spring.version>4.3.9.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<!--scope为test的话,发布后不发布-->
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Spring AOP所需 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
</dependencies>
|
- 编写Spring的配置文件 (XXX.xml,一般叫applictionContext.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-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <!--属性注入方式:要求有无参的构造方法,以及标准的get和set方法--> <!--默认构建的bean的单例模式-->
</beans>
|
第二节:XML方式注入bean
- 在applicationContext中bean标签
|
<bean id="p1" class="bean.Person"> <property name="name" value="张三"></property> <property name="age" value="10"></property> </bean> <!-- 构造函数注入方式,要求必须有自定义构造方法--> <bean id="p2" class="bean.Person"> <constructor-arg name="name" value="lisi"></constructor-arg> <constructor-arg name="age" value="20"></constructor-arg> </bean> <bean id="p3" class="bean.Person"> <constructor-arg index="0" value="wangwu"> </constructor-arg> <constructor-arg name="age" value="19"></constructor-arg> </bean>
|
- 在test文件中测试
|
public class MyTest { @Test public void test(){ //创建一个Spring的IOC容器 ApplicationContext appContext = new ClassPathXmlApplicationContext("applicationContext.xml"); Person person = (Person) appContext.getBean("p1"); System.out.println(person);
Person person1 = (Person) appContext.getBean("p1");; System.out.println(person==person1);
Person p2 = (Person) appContext.getBean("p2"); Person p3 = (Person) appContext.getBean("p3");
System.out.println(p2); System.out.println(p3); } }
|
- Xml文件中,使用ref引用引用类型
|
<bean id="car1" class="bean.Car"> <property name="brand" value="benz"></property> <property name="price" value="600000"></property> </bean> <bean id="d1" class="bean.Driver"> <property name="name" value="abc"></property> <property name="age" value="40"></property> <property name="car" ref="car1"></property> </bean>
|
Test中:
|
Driver d1 = (Driver)appContext.getBean("d1"); System.out.println(d1);
|
- 含有List成员的对象
|
public class DriverListCar extends Person { private List<Car> cars;
public DriverListCar() { }
|
|
<!-- 含有List成员--> <bean id="driverListCar" class="bean.DriverListCar"> <property name="name" value="a"></property> <property name="age" value="50"></property> <property name="cars" > <list> <!-- 引用已有的对象--> <ref bean="car1"></ref> <!-- 构造新对象--> <bean class="bean.Car"> <property name="brand" value="bmw"></property> <property name="price" value="350000"></property> </bean> </list> </property> </bean>
|
|
Test:
DriverListCar d1 = (DriverListCar) appContext.getBean("driverListCar"); System.out.println(d1);
|
- 含有Map成员的对象
|
public class DriverMapCar extends Person { private Map<String,Car> cars;
public DriverMapCar() { }
|
|
Xml文件:
<!-- 含有Map成员--> <bean id="driverMapCar" class="bean.DriverMapCar"> <property name="name" value="b"></property> <property name="age" value="50"></property> <property name="cars" > <map> <!-- 键值对形式--> <entry key="car1" value-ref="car1"></entry> <!-- 新构造一辆车 --> <entry key="car2"> <bean class="bean.Car"> <property name="brand" value="ford"></property> <property name="price" value="120000"></property> </bean> </entry> </map> </property> </bean>
|
|
Test:
DriverMapCar d2 = (DriverMapCar)appContext.getBean("driverMapCar"); System.out.println(d2);
|
- bean的属性
1) scope对象的作用域
(1) singleton:单例模式,默认
(2) prototype:原型的,容器初始化时不创建bean,每当请求bean实例时创建一个新的bean。【Controller层的类使用该作用域】
|
<!-- 测试scope属性--> <bean id="person1" class="bean.Person" scope="prototype"> <property name="name" value="zhangsan"></property> <property name="age" value="29"></property> </bean>
|
|
@Test public void testScope(){ ApplicationContext appContext = new ClassPathXmlApplicationContext("applicationContext.xml"); Person person1 = (Person)appContext.getBean("person1"); Person person2 = (Person)appContext.getBean("person1"); System.out.println(person1 == person2); }
|
结果为false
2) init-method和destroy-method(Ioc容器关闭回调该属性所描述的方法)
|
在实体类添加相应的init和destroy方法:
public void init(){ System.out.println("init"); }
public void destroy(){ System.out.println("destroy"); }
|
|
在xml文件中设置单例模式,并指明init-method和destroy-method所对应的方法:
<bean id="person1" class="bean.Person" init-method="init" destroy-method="destroy" > <property name="name" value="zhangsan"></property> <property name="age" value="29"></property> </bean>
|
|
Test:
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext("applicationContext.xml"); Person person1 = (Person) appContext.getBean("person1"); Person person2 = (Person) appContext.getBean("person1"); appContext.close();
|
|
单例模式时的输出:
|
Scope为Prototype时:
|
<bean id="person1" class="bean.Person" init-method="init" destroy-method="destroy" scope="prototype"> <property name="name" value="zhangsan"></property> <property name="age" value="29"></property> </bean>
|
|
Test不变,结果为:Person构造方法,setName和init都调用了两次,但是destroy方法没有调用。
|
3) autowire属性:自动装配属性,一个对象的属性为另一个对象时,可以通过自动autowire进行自动装配,而不需要再指明此对象的引用了
可以byName,byType
ByName形式:
|
实体类:
private String name; private String gender; private Integer age; private Car car;//实体类属性名称和xml相应对象的id一致,才能装配 private Book book;
|
|
XML文件
<bean id="book" class="com.iss.entity.Book"> <property name="name" value="java"></property> <property name="anthor" value="a"></property> </bean> <bean id="car" class="com.iss.entity.Car"> <property name="price" value="90"></property> <property name="brand" value="benz"></property> </bean>
<bean id="p" class="com.iss.entity.Person" autowire="byName" > <property name="name" value="zhangsan"></property> <property name="gender" value="nan"></property> </bean>
|
|
Test:
@Test public void test() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext1.xml"); Person p = (Person) context.getBean("p"); System.out.println(p);
context.close(); }
|
byType形式:找相应类型进行装配,如果都多个相同类型的bean,则会抛异常,实体类不变。
|
<bean id="book1" class="com.iss.entity.Book"> <property name="name" value="java"></property> <property name="anthor" value="a"></property> </bean> <bean id="car1" class="com.iss.entity.Car"> <property name="price" value="90"></property> <property name="brand" value="benz"></property> </bean> <!--实体类属性名称和相应对象id不同时,用byType也能装配--> <bean id="p" class="com.iss.entity.Person" autowire="byType" > <property name="name" value="zhangsan"></property> <property name="gender" value="nan"></property> </bean>
|
- bean的后置处理器:
如果需要在Spring容器中完成bean初始化方法前后添加额外的逻辑处理,可以定义一个或者多个 BeanPostProcessor 的派生类来完成,然后将该类注册到Spring的IOC容器中。
|
定义一个类,继承BeanPostProcessor
public class MyBeanPostProcessor implements BeanPostProcessor { public Object postProcessBeforeInitialization(Object bean, String s) throws BeansException { System.out.println("before:"+bean); return bean;
}
public Object postProcessAfterInitialization(Object bean, String s) throws BeansException {
System.out.println("after:"+bean);
return bean; } }
|
|
在xml文件中配置后置处理器:
<!--BeanPostProcessor后置处理器配置--> <bean class="processor.MyBeanPostProcessor"></bean>
|
|
输出结果:
|
第三节:注解形式注入bean
- 常用注解:Spring的配置文件中要加入自动进行组件扫描的配置【告诉Spring将哪些包下有注解的类完成bean的初始化】
<!-- 包名 可以描述组件所在的共同包部分,多个包可以用逗号分隔 -->
<context:component-scan base-package="包名 , ..."></context:component-scan>
常用的注解
(1) @Component : 普通类 , 如 Student com.isoft.bean , com.isoft.entity
(2) @Controller : 控制层 , Servlet , SpringMVC 等,com.isoft.servlet , com.isoft.controller
(3) @Service : 业务逻辑处理层,com.isoft.service
(4) @Respository : 持久层com.isoft.dao
以上注解都有value属性,当不适用value属性时,以上类创建的对象名或者beanId名将是,类名首字母小写作为对象名
(5) @Scope("prototype") :<bean scope="?">
(6) @Autowired:注解在成员变量上,默认按照类型匹配
通常情况下@Autowired是通过byType的方法注入的,可是在多个实现类的时候,byType的方式不再是唯一,而需要通过byName的方式来注入,而这个name默认就是根据变量名来的。
(7) @Qualified
(8) @PostConstruct:相当于<bean init-method="?">注解在方法上
(9) @PreDestroy: 相当于<bean destroy-method="?"> 注解在方法上
- Spring配置文件中添加包扫描,在applicationContext.xml
|
<!--配置自动扫描的包--> <context:component-scan base-package="com.iss"></context:component-scan>
|
- @Scope("prototype"):默认是单例模式,如果需要多个对象,添加此注解,但是如果多个对象,会使含有次对象的自动注入的其他对象,得不到这个参数。
|
实体类:
@Component(value = "car") @Scope(value = "prototype") public class Car implements Serializable { private String brand; private double price;
|
|
Service:
@Service public class CarService implements CarDao { @Autowired private Car car;
|
|
Test:ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Car car1 = (Car)context.getBean("car"); car1.setBrand("benz"); car1.setPrice(100000); Car c2 = (Car)context.getBean("car"); c2.setBrand("bmw"); c2.setPrice(200000); System.out.println(car1); System.out.println(c2); System.out.println(car1==c2); CarService carService = (CarService) context.getBean("carService"); System.out.println("service car = "+carService.getCar());
|
|
运行结果:
|
- @Autowired:在junit中不管用,需要在一个非测试类中使用才有效
1) @Autowired和@Resource的区别
@Autowired默认按照byType方式进行bean匹配,@Resource默认按照byName方式进行bean匹配
@Autowired是Spring的注解,@Resource是J2EE的注解,这个看一下导入注解的时候这两个注解的包名就一清二楚了
- 当使用@AutoWired注解时,如果找到两个或多个可匹配的对象,则可以使用
|
Person类:
private String name; private String gender; private Integer age; @Autowired private Car car; @Autowired private Book book;
|
|
@Component public class Car { private String brand; private Integer price;
|
|
@Component public class Book { private String name; private String anthor;
|
|
Test:
@Test public void test() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext1.xml"); Person p = (Person) context.getBean(Person.class); System.out.println(p); context.close(); }
|
- @ Qualifier指明要自动装配的对象:当有一个父类,多个子类时,如果用父类对象声明,而子类对象指明了name时,使用@AutoWried注解会报错,当子类对象没有指明name时,会自动装配父类对象,如果想装配某个子类对象,可以用@Qualified指明对应的name:
|
父类:
@Component(value = "book") public class Book { private String name; private String anthor;
|
|
子类:
@Component(value = "childrenBook") public class ChildrenBook extends Book { public void print(){ System.out.println("childrenBook"); } }
|
|
@Component public class Person { private String name; private String gender; private Integer age; @Autowired private Car car; @Autowired @Qualifier(value = "childrenBook") private Book book; }
|
|
Test:
@Test public void test() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext1.xml"); Person p = (Person) context.getBean(Person.class); p.getBook().print(); context.close(); }
|
|
结果:
|
- @PostConstruct和@PreDestroy,注意:prototype时,不用调用destroy方法
|
@PostConstruct public void init() { System.out.println("car init"); }
@PreDestroy public void destroy() { System.out.println("car destroy"); }
|
第三章:AOP
- 简介 :AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。
底层实现是使用代理模式完成。
使用 :事务处理、日志、权限认证等。
- AOP 是一种编程典范,它通过分离横切关注点来增加程序的模块化。简单说就是 AOP 可以在不修改现有代码的情况下对现有代码增加一些功能,那么这就是 AOP 最强大的功能。目前最受欢迎的 AOP 库有两个,一个是 AspectJ, 另外一个就是Spring AOP。
Spring框架的AOP机制可以让开发者把业务流程中的通用功能抽取出来,单独编写功能代码。在业务流程执行过程中,Spring框架会根据业务流程要求,自动把独立编写的功能代码切入到流程的合适位置。
- 相关术语
1) 横切关注点:对哪个类的哪个方法进行拦截,拦截后执行什么逻辑,这些关注点称为横切关注点。
2) 切面(Aspect):对横切关注点的抽象 -- 切面类定义 + 拦截谁 + 拦截后做什么
3) 连接点(Joinpoint):[被拦截到的方法]
4) 切点(pointcut):[拦截谁--方法] :需要配置切点表达式,定义拦截谁 --- 配置拦截哪个类的哪个方法
5) 通知(Advice):[拦截后做什么] : 拦截到指定方法后在什么位置执行什么逻辑处理
- AOP的实现-XML形式
1) 步骤
(1) 明确拦截谁(哪个类的那个方法|哪个类的哪些方法|哪些类的哪些方法),拦截方法的什么地方,拦截方法之后做什么。
(2) 定义拦截器类(切面类),普通类定义,加配置成为拦截器类,描述的是通知(方法),包括前置通知,后置通知,返回通知,异常通知,环绕通知。
(3) 配置:
l 切点表达式PointCut拦截谁
l 切面类bean生成
l 所要拦截的方法被调用了要在什么位置执行切面类的哪个方法
2) 编写需要拦截的类
|
@Service public class OprService { public int add(int n1,int n2){ System.out.println("Call addInt"); return n1+n2; }
public double add(double n1,double n2){ System.out.println("Call addDouble"); return n1+n2; } public boolean fun(String info) throws NullPointerException{ System.out.println("Call fun"); if(info == null || info.trim().length()==0){ throw new NullPointerException("参数为空"); } return true; } }
|
3) 编写切面类,前置通知,后置通知,返回通知,异常通知
|
@Component(value = "aspect1") public class Aspect1 { public Aspect1(){}
public void before(JoinPoint point) { System.out.println("AOP : before"); getHandler(point); }
public void after(JoinPoint point) { System.out.println("AOP : after"); getHandler(point); }
//参数列表的JoinPoint,和Object的顺序不能颠倒,第一个参数必须是JoinPoint public void afterRetuning(JoinPoint point,Object o) { System.out.println("AOP : afterRetuning"); getHandler(point); System.out.println("返回值:" + o); } a public void exp(JoinPoint point,Exception e) { System.out.println("AOP : exp"); getHandler(point); System.out.println("异常信息为:" + e); }
private void getHandler(JoinPoint point) { Object[] params = point.getArgs();//获取拦截到的方法的参数列表 String methodName = point.getSignature().getName();//获取所拦截方法的名字 Class clazz = point.getTarget().getClass();//获取拦截方法所属的类 System.out.println("拦截到了:" + clazz.getPackage() + "." + clazz.getSimpleName() + "." + methodName); System.out.println("该方法的参数列表为:"); for (Object o : params) { System.out.println(o); } } }
|
|
4) 环绕通知
|
/** * 环绕通知 */ @Component public class Aspect2 { public Object around(ProceedingJoinPoint point) { Object result = null; try { System.out.println("此处写前置通知"); result = point.proceed(); System.out.println("此处写后置通知"); } catch (Throwable e) { System.out.println("此处写异常通知"); return null; } System.out.println("此处写返回通知:"+result); return result; } }
|
5) Xml文件中设置关于AOP的配置信息
|
<!--配置自动扫描的包--> <context:component-scan base-package="com.iss"></context:component-scan>
<aop:config> <!-- *号通配符表示任意返回值类型--> <aop:pointcut id="oprservice.add" expression="execution(* com.iss.service.OprService.add(..))"/> <aop:pointcut id="oprservice.fun" expression="execution(public boolean com.iss.service.OprService.fun(..))"/> <!-- 配置切面类--> <aop:aspect ref="aspect1" order="1"> <aop:before method="before" pointcut-ref="oprservice.add"></aop:before> <aop:after method="after" pointcut-ref="oprservice.add"></aop:after> <aop:after-returning method="afterRetuning" pointcut-ref="oprservice.fun" returning="o"></aop:after-returning> <aop:after-throwing method="exp" pointcut-ref="oprservice.fun" throwing="e"></aop:after-throwing> </aop:aspect>
<!-- 环绕通知配置--> <aop:aspect ref="aspect2" order="2"> <aop:around method="around" pointcut-ref="oprservice.fun"></aop:around> </aop:aspect> </aop:config>
|
6) 测试:
|
@Test public void test() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); OprService oprService = (OprService) context.getBean("oprService"); int add1 = oprService.add(10, 20); System.out.println("add result :" + add1); //返回通知 boolean r = oprService.fun("hello"); System.out.println("r=" + r); try { boolean r1 = oprService.fun(""); } catch (Exception e) {
}
|
- AOP的实现-注解形式
1) 同上,删除xml文件中关于aop的配置,并添加如下代码
Xml文件中,加上自动扫描切面包
|
<!--自动扫描切面类--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
|
2) 在切面类里添加注解,不用改代码,只需要加入关于切面的注解
|
@Component(value = "aspect1") @Aspect @Order(value = 1) public class Aspect1 {
//设置切点 @Pointcut("execution(* com.iss.service.OprService.add(..))") public void proinCutAdd(){
}
@Pointcut("execution(boolean com.iss.service.OprService.fun(..))") public void proinCutFun(){
} public Aspect1(){}
@Before(value = "proinCutAdd()") public void before(JoinPoint point) { System.out.println("AOP : before"); getHandler(point); }
@After(value = "proinCutAdd()") public void after(JoinPoint point) { System.out.println("AOP : after"); getHandler(point); }
//参数列表的JoinPoint,和Object的顺序不能颠倒,第一个参数必须是JoinPoint @AfterReturning(value = "proinCutFun()",returning = "o") public void afterRetuning(JoinPoint point,Object o) { System.out.println("AOP : afterRetuning"); getHandler(point); System.out.println("返回值:" + o); }
@AfterThrowing(value = "proinCutFun()",throwing = "e") public void exp(JoinPoint point,Exception e) { System.out.println("AOP : exp"); getHandler(point); System.out.println("异常信息为:" + e); }
private void getHandler(JoinPoint point) { Object[] params = point.getArgs();//获取拦截到的方法的参数列表 String methodName = point.getSignature().getName();//获取所拦截方法的名字 Class clazz = point.getTarget().getClass();//获取拦截方法所属的类 System.out.println("拦截到了:" + clazz.getPackage() + "." + clazz.getSimpleName() + "." + methodName); System.out.println("该方法的参数列表为:"); for (Object o : params) { System.out.println(o); } } }
|
3) 测试:
|
@Test public void test() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); OprService oprService = (OprService) context.getBean("oprService"); int add1 = oprService.add(10, 20); System.out.println("add result :" + add1); //返回通知 boolean r = oprService.fun("hello"); System.out.println("r=" + r); try { boolean r1 = oprService.fun(""); } catch (Exception e) {
}
|
4) 环绕切面类
|
/** * 环绕通知 */ @Component @Aspect @Order(value = 2) public class Aspect2 { @Pointcut("execution(boolean com.iss.service.OprService.fun(..))") public void proinCutFun(){
} @Around(value = "proinCutFun()") public Object around(ProceedingJoinPoint point) { Object result = null; try { System.out.println("此处写前置通知"); result = point.proceed(); System.out.println("此处写后置通知"); } catch (Throwable e) { System.out.println("此处写异常通知"); return null; } System.out.println("此处写返回通知:"+result); return result; } }
|