spring
- ssh: struts2+hibernate+spring
- ssm: spring+springmvc+mybatis
问题:什么是spring?
答:spring是一个轻量级的J2EE框架,它可以让java的企业级开发变的非常简单
问题:spring有哪些版本?
答:1.2,2.0,2.5,3.0,3.1,4.0,4.2,5.x
我们学习阶段:刚开始使用:4.2,使用maven以后版本改为4.37,在微服务架构中我们使用 5.1.6
spring提倡的一个思想,组件与组件之间尽量做到低耦合
准备工作:
在idea中,配置applicationContext.xml文件的模板
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
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/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.2.xsd ">
</beans>
spring的核心概念:
IOC : 控制反转
它是一种思想,它是指创建对象、管理对象的方式,以前是自己创建自己管理,现在变成由spring容器创建,容器进行管理
DI : 依赖注入
它是IOC的具体实现,有了spring以后,类中属性的初始化,依赖于spring容器注入的参数,才可以完成初始化,这一个
过程就称为:“依赖注入”
AOP :面向切向编程
在没有spring之间,假设:一个层次要调用另一个层次的方法(例如:Service调用Dao层的方法),可以在一个类,
自己创建另一个类的实例
public class InfService {
private InfDao dao;
public void setDao(InfDao dao) {
this.dao = dao;
}
public void addData(){
dao.add();
}
}
@@@@@@@:这种new对象的写法,属于一种高耦合的写法
在spring应用中,不提倡,调用者自己创建被调用者的实例,在spring的应用中,所有对象都是由spring容器来创建,
哪里,需要这个对象, spring容器就会把对象传递到哪里
-----------------------------------------------------------------------
任务1:通过spring实现hello world
步骤:
1、创建一个java工程
2、导入jar
spring 4.2.4
core,beans,context,expression
sprign 3.0.2
logging (spring-framework-3.0.2.RELEASE-dependencies\org.apache.commons\com.springsource.org.apache.commons.logging\1.1.1)
3、生成spring的主配置文件 applicationContext.xml
4、创建InfDao
package org.java.dao;
public class InfDao {
public void add(){
System.out.println("向mysql添加了数据");
}
}
5、在applicationContext.xml文件中,注册InfDao
<bean id="infDao" class="org.java.dao.InfDao"/>
6、创建InfService
public class InfService {
private InfDao dao;
public void setDao(InfDao dao) {
this.dao = dao;
}
public void addData(){
dao.add();
}
}
7、在applicationContext.xml文件中,配置InfService,并且配置要给当前对象注入的对象
<bean id="infService" class="org.java.service.InfService">
<property name="dao" ref="infDao"/>
</bean>
8、编写测试类进行测试
package org.java.demo;
import org.java.service.InfService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Demo {
public static void main(String[] args) {
ApplicationContext cxf = new ClassPathXmlApplicationContext("applicationContext.xml");
InfService service = (InfService) cxf.getBean("infService");
service.addData();
}
}
ApplicationContext 与 applicationContext.xml是什么关系 ?
ApplicationContext:它是spring容器,它负责产生对象,只有在applicationContext.xml文件中,注册过的对象,它才能产生
问题:为什么要使用依赖注入DI
答:目的,将组件的创建与使用强制分开,降低组件与组件之间的耦合度
我们要使用某一个组件,只需要声明即可,不需要关心该组件如合创建,在运行时,spring容器会把创建好的组件
自动注入到需要它的地方
DI:它是一种组件化的思想,它可以将一个复杂应用,按功能拆分
spring应用,提倡面向接口编程:
- 可以隐藏实现细节
- 使用接口可以方便更换程序中的业务逻辑
注意:一般Service,Dao这两层都要写接口
接口的写法:
书上的规范:
- 接口名: org.java.dao.IDao
- 实现类: org.java.dao.Dao
开发中的写法:
- 接口名: org.java.dao.InfDao
- 实现类: org.java.dao.impl.InfDaoImpl
spring:按注入的数据类型,可以分为:
1、对象的注入 ref
<bean id="infService" class="org.java.service.impl.InfServiceImpl">
<property name="dao" ref="infDao"/>
</bean>
2、值的注入 value
<bean id="infService" class="org.java.service.impl.InfServiceImpl">
<property name="dao" ref="infDao"/>
<property name="msg" value="hello world"/>
<property name="score" value="100"/>
</bean>
3、集合的注入 (List,Map,Set)
@@@list:
<bean id="infService" class="org.java.service.impl.InfServiceImpl">
<property name="list">
<list>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</list>
</property>
</bean>
@@@@Set
<bean id="infService" class="org.java.service.impl.InfServiceImpl">
<property name="arr">
<set>
<value>111</value>
<value>222</value>
<value>333</value>
</set>
</property>
</bean>
@@@@ Map
<bean id="infService" class="org.java.service.impl.InfServiceImpl">
<property name="map">
<props>
<prop key="msg1">one.....</prop>
<prop key="msg2">two.....</prop>
</props>
</property>
</bean>
- 值:value
- 对象: ref
- 集合:list,set,props
spring按注入的数据的方式,又可分为:
1、依赖注入
@@@:这种方式是通过属性的set方法进行注入
private InfDao dao;
public void setDao(InfDao dao) {
this.dao = dao;
}
2、构造注入
@@@:这种方式是通过构造方法进行注入
需要在类中,编写一个带参数的构造方法
public InfService(InfDao dao,String msg, int score) {
this.dao = dao;
}
<bean id="infService" class="org.java.service.InfService">
<constructor-arg index="0" ref="infDao"/>
<constructor-arg index="1" value="hello"/>
<constructor-arg index="2" value="100"/>
</bean>
3、自动绑定注入
@@@@@:这种方式注入,属性依然要生成set方法
<bean id="infService" class="org.java.service.InfService" autowire="byType"/>
这种写法,需要什么类型,自己在spring容器去找对应的类型,然后自己注入
- utowire="byType"-------这种方式是按类型,自动注入,要求:匹配类型只有一个才可以使用
<bean id="infService" class="org.java.service.InfService" autowire="byName"/>
- 这种方式按名称匹配,自动注入,允许有多个匹配类型
在spring应用中,为了简化开发过程,简化xml文件中的代码,spring提供了一种注解的方式
Annotations: 注释,注解
使用spring注解(spring注释)的好处:
- 代码会更为简单
- 简化了xml文件的代码,很多配置无需在xml文件中执行
对象不需要在xml文件中注册
对象之间的依赖关系,也不需要在xml文件中进行配置 - 对象的注入,不需要写set方法
采用spring注解的方式配置程序:
步骤:
1、创建java工程
2、导入jar
spring 4.2
core,beans,context,expression
增加: aop,aspects
spring 3.0.2
logging
增加: aop联盟 aopalliance,
3.0.2.RELEASE-dependencies\org.aopalliance\com.springsource.org.aopalliance\1.0.0
织入的包 weaver
3.0.2.RELEASE-dependencies\org.aspectj\com.springsource.org.aspectj.weaver\1.6.8.RELEASE
3、编写applicationContext.xml
4、在xml文件启用spring注解
<context:annotation-config/>
spring中的注解符号:
- <context:annotation-config/> 该符号启用spring注解
- <context:component-scan base-package="org.java"/> 组件扫描,扫描org.java包下面所有加了注解标识的类
由于<context:component-scan base-package="org.java"/>它是继承于<context:annotation-config/>
所以,只要采用组件扫描,<context:annotation-config/>这个标记,可以省略不写
- 数据访问层(Dao)------------------ @Repsoitory
- 业务逻辑层 (service)--------------- @Service
- 控制层 (Web) ------------------ @Controller
- 早期的用于标识组件的注解符号是: @Compoment (它可以表示:数据访问层,业务逻辑层,控制层)
@Autowired
private InfDao dao;-----------此代码,表示,按类型自动注入byType
@@@@@@@@如果采用这一种方式,注入,要求,匹配的类型,只能有一个
@Autowired
@Qualifier("dao2")
private InfDao dao; ----------此代码,表示,按照名称,自动注入 byName
在使用spring注解时,如果没有给当前组件指定别名,系统将生成,默认的别名:
生成的原则如下: 类名,首字母小写
注意:一旦给组件指定别名,默认别名就失效
AOP:面向切面编程(面向方面编程)
问题1:什么是aop?
答:AOP称为“面向切面编程”,也称为“面向方面编程”
问题2:aop要解决什么样的问题?
答:aop用于集中处理程序中涉及到公共性的一些辅助功能,让程序员集中精力编写业务逻辑
问题3:aop通过什么方式来实现?
答:aop主要是通过一种“通知”的形式来实现。
通知的类型有四种:它们会在不同的时候,自动的调用
- 前置通知、后置通知、异常通知、环绕通知
问题4:通知应该如何配置?
答:要配置通知,我们要通过一种"代理模式"的方式进行配置
问题5:为什么要用代理模式?(proxy)
答:通过代理模式,可以在实现原有功能的基础上,附加额外功能
- 核心任务:将程序中涉及到的公共问题,集中进行处理
将程序中,涉及到的一些辅助性的一些公共问题,集中编写成一个公共的模块,通过一定的配置,当需要这些功能的时候,
系统将会自动进行调用
AOP实现方式,主要是通过一种"通知"的方式进行实现 (Advice)
spring的AOP提供了四种类型的通知,分别在不同的时候自动运行:
- 前置通知--------------------配置在前置通知的代码,将会在业务逻辑执行之前自动调用
- 后置通知--------------------配置在后置通知的代码,将会在业务逻辑执行之后自动调用
- 异常通知--------------------配置在异常通知的代码,将会在业务逻辑执行过程中,产生异常时执行
- 环绕通知--------------------它把整个业务逻辑执行过程包含在通知中
@@@@@@@@@如何配置通知
在AOP中,配置通知,需要使用一种设计模式:代理模式(proxy)
代理模式:通过代理可以在实现原有功能的基础上,附加额外功能
@@@@@@@:使用代理模式的目的,要在原有功能的基础上,附加额外功能
示例:
applicationContext.xml
<!--业务类-->
<bean id="buyBookService" class="org.java.service.impl.BuyBookServiceImpl"/>
<!--声明通知 -->
<bean id="before" class="org.java.advice.BeforeAdvice"/>
<bean id="after" class="org.java.advice.AfterAdvice"/>
<!--配置代理类-->
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--指定访问哪一个接口时,进行代理-->
<property name="proxyInterfaces">
<value>org.java.service.BuyBookService</value>
</property>
<!--指定,要附加的功能-->
<property name="interceptorNames">
<list>
<value>before</value>
<value>after</value>
</list>
</property>
<!--指定,功能附加完成以后,要调用的业务类-->
<property name="target" ref="buyBookService"/>
</bean>
AfterAdvice.java
package org.java.advice;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class AfterAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("");
System.out.println("------后置通知----------");
}
}
BeforeAdvice.java
package org.java.advice;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* 前置通知,此处的代码,在执行业务逻辑代码之前,自动运行
*/
public class BeforeAdvice implements MethodBeforeAdvice {
/**
*
* @param method:即将执行的是类中的哪一个业务方法
* @param objects:调用方法时,要传递给方法的参数有哪些
* @param o:即将要执行的是哪一个业务逻辑类
* @throws Throwable
*/
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("-------进入前置通知类-------------");
System.out.println("即将访问的业务类是:"+o.getClass());
System.out.println("即将访问的业务方法是:"+method.getName());
System.out.println("即将传递给方法的参数有:"+ Arrays.toString(objects));
System.out.println("-------离开前置通知类-------------");
System.out.println("");
}
}
BuyBookServiceImpl.java
package org.java.service.impl;
import org.java.service.BuyBookService;
/**
* 业务实现类
*/
public class BuyBookServiceImpl implements BuyBookService {
@Override
public String buyBook(String who, String bookName) {
System.out.println("-----------------进入业务逻辑类------------------");
System.out.println("正在进行业务逻辑处理");
System.out.println("-----------------离开业务逻辑类------------------");
return who+",购买了书:"+bookName;
}
}
测试
package org.java.demo;
import org.java.service.BuyBookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Demo {
public static void main(String[] args) {
ApplicationContext cxf = new ClassPathXmlApplicationContext("applicationContext.xml");
BuyBookService service = (BuyBookService) cxf.getBean("proxy");
service.buyBook("张三","环球地理");
}
}
前置通知:它会在业务逻辑类调用之前先执行,前置通知需要实现接口: MethodBeforeAdvice
后置通知:它会在业务逻辑类调用之后执行,后置通知,需要实现接口:AfterReturningAdvice
异常通知:它会在业务逻辑类执行过程中,如果产生了异常才会被调用,异常通知,需要实现接口ThrowsAdvice
异常通知中,没有强制实现的方法,但我们写的方法要满足以下规则:
public void afterThrowing(Method method,Object[] args,Object target,Exception ex ){
}
环绕通知:它把整个业务逻辑类的执行包含在通知里面,环绕通知,一般实现:MethodInterceptor
目前,这一种代理方式存在的问题:
- 问题1: 针对于每一个业务类配置一个代理,如果有1000个业务类,就需要配置1000个代理类
- 问题2: 这种方式配置代理,每一次都需要访问代理类的名称,才能有附加功能
解决方案1:
采用自动代理
优点:
- 代理由系统根据访问的业务的名称,自动创建代理
- 直接访问业务类的名称即可,不用访问代理类的名称
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<!--要附加的功能-->
<property name="interceptorNames">
<list>
<value>before</value>
<value>after</value>
<value>mythrows</value>
</list>
</property>
<!--配置对哪些业务类进行代理-->
<property name="beanNames">
<list>
<value>buyBookService</value>
<value>service2</value>
<value>service3</value>
</list>
</property>
</bean>
解决方案2:
声明式代理
优点:
- 对某一个包路径下面的所有业务类,形成代理(无需修改代码)
- 直接访问业务类的名称即可,不用访问代理类的名称
- 采用声明式代理,通知写在一个类中即可,这个类,无需实现任何接口
<!--AOP配置-->
<aop:config>
<!--配置切入点(触发代理的条件)-->
<aop:pointcut id="beanNames" expression="execution(* org.java.service.*.*(..))"/>
<!--配置切面-->
<aop:aspect ref="advice">
<!--<aop:before method="before" pointcut-ref="beanNames"/>-->
<!--<aop:after-returning method="after" returning="returnValue" pointcut-ref="beanNames"/>-->
<!--<aop:after-throwing method="mythrowing" pointcut-ref="beanNames" throwing="ex"/>-->
<aop:around method="round" pointcut-ref="beanNames"/>
</aop:aspect>
</aop:config>
解决方案3:采用注解的方式配置
<!--组件扫描-->
<context:component-scan base-package="org.java"/>
<!--指定,采用注解的方式,配置通知-->
<aop:aspectj-autoproxy/>
@Before(value = "execution(* org.java.service.*.*(..))")
public void before(JoinPoint jp){
System.out.println("------------------------进入前置通知");
}