Spring核心概念
初始Spring
在学习Spring之前让我们先了解一下企业级应用.企业级应用是指那些为商业组织,大型企业而创建并部署的解决方案及应用,这些大型企业级应用的结构复杂,涉及的外部资源众多,事务密集,数据规模大,用户数量多.有较强的安全性考虑和较高的性能要求,而Spring就是用来做解决这些问题的
Spring是一个轻量级的框架,是当前的主流框架,它能使现有技术更加易用,推进编码最佳实践
Spring坚持一个原则:不重新发名轮子 Spring由大约20个模块组成。
分成六个部分:Core Container、DataAccess/Integration、Web、AOP、Instrumentation、Test。
Spring Core是框架的最基础的部分,提供了IOC特性
Spring Context为企业级开发提供了便利和集成的工具
Spring AOP是基于Spring Core的符合规范的面向切面编程的实现。
Spring JDBC提供了JDBC的抽象层,简化了JDBC编码
Spring ORM对市面上流行的ORM框架提供了支持
Spring Web 为Spring在Web应用程序中的使用提供了支持
Spring IOC
理解控制反转
控制反转(Inversion of Control,IoC),也称为依赖注入(Dependency Injection,DI),是面向对象编程中的一种设计理念,用来降低代码之间的耦合度
首先考虑什么是依赖,例如:在A类方法中,实例化了B类的对象并调用其方法以完成特定的功能,我们就
说A类依赖于B类,下面我们通过示例理解一下.
定义一个接口
public interface UserDao{
/**
*保存用户信息的方法
*/
public void save(User user);
}
接口的实现类
pulbic class UserDaoImpl implements UserDao{
public void save(User user){
System.out.print("保存用户信息到数据库");
}
}
用户的业务类
public class UserServiceImpl implements UserService{
//实例化所依赖的UserDao对象
private UserDao dao=new UserDapImpl();
//调用UserDao的方法保存用户信息
dao.save(User);
}
以上代码所示,UserServiceImpl对UserDaoImpl存在依赖关系,这样的代码很常见,但是存在一个严重的问题,就是这个两个类是高度耦合,如果我们有新的需求需要替换UserDao的实现类,导致UserServiceImpl里面的代码也要随之发生改变,这种代码不具有优良的可扩展性和可维护性,甚至在开发中难以测试
我们可以利用简单的工厂和工厂模式的思路解决此类问题
public class UserDaoFactory{
public static UserDao getInstance(){
//具体实现过程省略
}
}
public class UserServiceImpl implements UserService{
//通过工厂获取所依赖的用户DAO对象
private UserDao dao=UserDaoFactory.getInstance();
public void addNewUser(User user){
//调用用户DAO的方法保存用户信息
dao.save(user);
}
}
这里的用户DAO工厂类UserDaoFactory就体现了"控制反转的思想":UserServiceImpl不再依赖自身的代码去获得所依赖的具体DAO对象,而是把这一工作转交给了第三方--UserDaoFactory,从而避免了和具体UserDao实现类之间的耦合
下面我们用Spring IOC 来实现
首先我们要下载Spring的jar包
jar包下载地址 我这边是用的版本是4.3.18
解压后的文件几个重要文件夹的意思:
docs:该文件夹下包含Spring的相关文档,包括API参考文档,开发手册
libs:该文件夹下存放Spring各个模块的jar文件,每个模块均提供三项内容,开发所需的jar文件,以"-javadoc"后缀表示的API和以"-sources"后缀表示的源文件
schema:配置Spring的某些功能时需要用到的schema文件,对于已经集成了Spring的IDE环境(如MyEclipse,IDEA),这些文件不需要专门导入
我们将jar导入到项目中
为项目添加log4j.properties文件,用来控制日志输出 文件放到resources里面
# 下面的属性配置中,所有日志的输出级别是info,输出源是con
log4j.rootLogger=info,error,con
#定义输出源的输出位置是控制台
log4j.appender.con=org.apache.log4j.ConsoleAppender
#定义输出日志的布局采用的类
log4j.appender.con.layout=org.apache.log4j.PatternLayout
#定义日志输出布局
log4j.appender.con.layout.ConversionPattern=%d{MM-dd HH:mm:ss}{%p}%c%n -%m%n
编写HelloSpring类
package cn.springdemo;
/**
* 第一个Spring,输出"Hello,Spring!"。
*
* @author 安心
*/
public class HelloSpring {
// 定义who属性,该属性的值将通过Spring框架进行设置
private String who = null;
/**
* 定义打印方法,输出一句完整的问候。
*/
public void print() {
System.out.println("Hello," + this.getWho() + "!");
}
/**
* 获得 who。
*
* @return who
*/
public String getWho() {
return who;
}
/**
* 设置 who。
*
* @param who
*/
public void setWho(String who) {
this.who = who;
}
}
在resources下面创建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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 通过bean元素声明需要Spring创建的实例。该实例的类型通过class属性指定,并通过id属性为该实例指定一个名称,以便在程序中使用 -->
<bean id="helloSpring" class="cn.springdemo.HelloSpring">
<!-- property元素用来为实例的属性赋值,此处实际是调用setWho()方法实现赋值操作 -->
<property name="who">
<!-- 此处将字符串"Spring"赋值给who属性 -->
<value>Spring</value>
</property>
</bean>
</beans>
在Spring配置文件中,使用
经验:
(1) 使用
(2) 在本例中,Spring为Bean的属性赋值是通过调用属性的setter方法实现的,这种做法称为“设值注入”,而非直接为属性赋值。若属性名为who,但是setter方法名为
setSomebody(),Spring配置文件中应写成name="somebody"而非name="who"。所以在为属性和setter访问器命名时,一定要注意遵循JavaBean的命名规范。
编写测试方法
public void helloSpring() {
// 通过ClassPathXmlApplicationContext实例化Spring的上下文
ApplicationContext context = new ClassPathXmlApplicationContext(
"applicationContext.xml");
// 通过ApplicationContext的getBean()方法,根据id来获取bean的实例
HelloSpring helloSpring = (HelloSpring) context.getBean("helloSpring");
// 执行print()方法
helloSpring.print();
}
测试代码中的ApplicationContext是一个接口,负责读取Spring配置文件,管理对象的加载,生成,维护Bean对象与Bean对象之间的依赖关系,负责Bean的生命周期等,ClassPathXmlApplicationContext是ApplicationContext接口的实现类,用于从classpath路径中读取Spring配置文件
Spring AOP
理解面向切面
面向切面编程(Aspect Oriented Programmin ,AOP)是软件编程发展到一定阶段的产物,AOP一般适用于具有横切逻辑的场合,如访问控制、事务管理、性能检测等。
面向切面编程,简单的说就是在不改变原程序设计的基础上位代码段增加新的功能,对代码段进行增强处理。设计思想来源于代理设计模式。
直接调用对象的方法
在代理模式中可以为该对象设置一个代理对象,代理对象为fun()提供一个代理方法,当通过代理对象的fun()方法调用原方法的fun()方法时,就可以在代理方法中添加新的功能,也就是所谓的增强处理。
通过代理对象调用方法
AOP相关术语
增强处理(Advice)
前置增强
后置增强
环绕增强、异常抛出增强、最终增强等类型
1、横切关注点
对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
2、切面(aspect)
类是对物体特征的抽象,切面就是对横切关注点的抽象
3、连接点(joinpoint)
被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器
4、切入点(pointcut)
对连接点进行拦截的定义
5、通知(advice)
所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类
6、目标对象
代理的目标对象
7、织入(weave)
将切面应用到目标对象并导致代理对象创建的过程
8、引入(introduction)
在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段
下面我们写一个使用Spring AOP实现日志输出也就是我们的log4j
1.我们先在项目中加入jar包
spring-aop 这个jar包 这个jar包给我们提供了AOP的实现,同时,Spring AOP还依赖AOP Alliance(aopalliance-1.0.jar)和AspectJ(aspectjweaver-1.6.9.jar)项目中的组件
接下来编写业务类UserServiceImpl
/**
* 用户业务类,实现对User功能的业务管理
*/
public class UserServiceImpl implements UserService {
// 声明接口类型的引用,和具体实现类解耦合
private UserDao dao;
// dao 属性的setter访问器,会被Spring调用,实现设值注入
public void setDao(UserDao dao) {
this.dao = dao;
}
public void addNewUser(User user) {
// 调用用户DAO的方法保存用户信息
dao.save(user);
}
}
该项目中有一个addNewUser()方法,实现添加用户的业务,接下来就以AOP的方式为该方法添加日志功能
编写增强类
public class UserServiceLogger {
private static final Logger log = Logger.getLogger(UserServiceLogger.class);
public void before(JoinPoint jp) {
log.info("调用 " + jp.getTarget() + " 的 " + jp.getSignature().getName()
+ " 方法。方法入参:" + Arrays.toString(jp.getArgs()));
}
public void afterReturning(JoinPoint jp, Object result) {
log.info("调用 " + jp.getTarget() + " 的 " + jp.getSignature().getName()
+ " 方法。方法返回值:" + result);
}
}
before()方法也就是在我们代码执行之前运行,afterReturning()在我们执行代码之后运行.Join Point类型的参数,Spring会自动注入该实例,通过getTarget()方法可以得到被代理的目标对象,getSignature()方法返回被代理的目标方法,getArgs()方法返回传递给目标方法的参数数组
在Spring配置文件中对相关组件进行声明
<bean id="dao" class="dao.impl.UserDaoImpl"> </bean>
<bean id="service"
class="service.impl.UserServiceImpl">
<property name="userDao" ref="dao"></property>
</bean>
<bean id="theLogger" class="aop.UserServiceLogger"></bean>
接下来在Spring配置文件中进行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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="dao" class="com.jikedaquan.spring.dao.impl.UserDaoImpl"> </bean>
<bean id="service" class="service.impl.UserServiceImpl">
<property name="userDao" ref="dao"></property>
</bean>
<bean id="theLogger" class="aop.UserServiceLogger"></bean>
<aop:config>
<!--定义一个切入点表达式,并命名为“pointcut”-->
<aop:pointcut id="pointcut"
expression="execution(public void addNewUser(entity.User))"/>
</aop:config>
</beans>
与AOP相关的配置都放在aop:config标签中,如配置切入点的标签aop:pointcut
execution是切入点指示符,切入点表达式支持模糊匹配
- public * addNewUser(entity.User):* 表示匹配所有类型的返回值
- public void * (entity.User):* 表示匹配所有方法名。
- public void addNewUser(..):.. 表示匹配所有参数个数和类型
-
- com.service.* .*(..):匹配com.service包下所有类的所有方法
-
- com.service..* .(..):匹配com.service包及其子包下所有类的所有方法 (前面句号就是 不知道为啥 *显示不出来 应该是Markdown的某个命令符号把)
大家可以根据自己的需求来设置切入点的匹配规则,当然配置的关键字还有很多,我就不一一介绍了,如果有兴趣可以查看Spring的开发手册
最后还需要在切入点处插入增强处理,这个过程的专业叫法是”织入“
<?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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="dao" class="com.jikedaquan.spring.dao.impl.UserDaoImpl"> </bean>
<bean id="service" class="service.impl.UserServiceImpl">
<property name="userDao" ref="dao"></property>
</bean>
<bean id="theLogger" class="aop.UserServiceLogger"></bean>
<aop:config>
<!--定义一个切入点表达式,并命名为“pointcut”-->
<aop:pointcut id="pointcut" expression="execution(public void addNewUser(entity.User))"/>
<!--引用包含增强方法的Bean-->
<aop:aspect ref="theLogger">
<!--将before()方法定义为前置增强并引用pointcut切入点-->
<aop:before method="before" pointcut-ref="pointcut"></aop:before>
<!--将afterReturning()方法定义为后置增强并引用pointcut切入点-->
<aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"></aop:after-returning>
</aop:aspect>
</aop:config>
</beans>
编写测试
ApplicationContext context=new
ClassPathXmlApplicationContext("applicationContext.xml");
UserService service= (UserService) context.getBean("service");
User user =new User();
service.addNewUser(null);
下一篇文章还有很详细的讲解IOC和AOP一些功能,不足的地方欢迎大家提出意见
by安心