目 录
一、 Spring概述1
1.1 Spring框架的作用1
1.2 Spring框架的优点1
1.3 Spring框架的容器1
二、 Spring容器的基本应用2
2.1如何将一个Bean组件交给Spring容器2
2.2如何获取Spring容器对象和Bean对象2
2.3如何控制对象创建的模式2
2.4 Bean对象创建的时机2
2.5为Bean对象执行初始化和销毁方法2
2.6案例:Spring框架的使用以及2.1节-2.5节整合测试3
三、 Spring框架IoC特性5
3.1 IoC概念5
3.2 DI概念5
3.3案例:测试IoC(set注入)5
3.4案例:测试IoC(构造注入)6
3.5案例:不用JDBC访问数据库,而是采用Hibernate访问6
Java
四、 Spring中各种类型的数据注入7
4.1 Bean对象注入7
4.2基本数据的注入7
4.3集合的注入7
4.4案例:各类数据注入7
五、 AOP概念10
5.1什么是AOP10
5.2 AOP和OOP的区别10
5.3 AOP相关术语10
5.4案例:AOP的使用,模拟某些组件需要记录日志的功能11
5.5通知类型11
5.6切入点12
5.7案例:环绕通知,修改5.4案例使之动态显示所执行的操作12
5.8案例:利用AOP实现异常处理,将异常信息写入文件13
六、 Log4j日志记录工具14
6.1 Log4j介绍14
6.2 Log4j的使用14
6.3案例:修改5.8案例,使用Log4j记录日志14
一、Spring概述
1.1 Spring框架的作用
Spring框架主要负责技术整合(可以整合很多技术),该框架提供IoC和AOP机制,基于这些特性整合,可以降低系统组件之间的耦合度,便于系统组件的维护、扩展和替换。
1.2 Spring框架的优点
其实与Spring框架的作用相同:
在SSH中,主要是利用Spring容器管理我们程序中的Action、DAO等组件,通过容器的IoC机制,可以降低Action、DAO之间的耦合度(关联度),利用AOP进行事务管理等共通部分的处理。
在SSH中,Struts2主要是利用它的控制器,而不是标签、表达式;Hibernate主要利用它的数据库访问;Spring主要是利用它的整合。
1.3 Spring框架的容器
Spring框架的核心是提供了一个容器(是我们抽象出来的,代指后面的类型)。该容器类型是BeanFactory或ApplicationContext(建议用这个类型,它是BeanFactory的子类,功能更多)。
该容器具有以下功能:
1)容器可以创建和销毁组件对象,等价于原来“工厂”类的作用。
2)容器可以采用不同的模式创建对象,如单例模式创建对象。
3)容器具有IoC机制实现。
4)容器具有AOP机制实现。
Java
二、
Spring容器的基本应用
2.1如何将一个Bean组件交给Spring容器
1)Bean组件其实就是个普通的Java类!
2)方法:在applicationContext.xml中添加以下定义,见2.6案例中step4。
2.2如何获取Spring容器对象和Bean对象
1)实例化容器:
ApplicationContext ac=new ClassPathXmlApplicationContext("/applicationContext.xml");
//FileSystemXmlApplicationContext("");//去指定的磁盘目录找,上面的为去Class路径找
2)利用getBean("标识符")方法获取容器中的Bean对象。见2.6案例中step5。
2.3如何控制对象创建的模式
Spring支持singleton(单例)和prototype(原型,非单例)两种模式。
默认是singleton模式,可以通过的scope属性修改为prototype模式。以后在Web程序中,通过扩展可以使用request、session等值。见2.6案例中step4、step7。
例如:
u 注意事项:对于NetCTOSS项目,一个请求创建一个Action,所以用Spring时必须指明prototype,否则默认使用singleton会出问题。而DAO则可用singleton模式。
2.4 Bean对象创建的时机
1)singleton模式的Bean组件是在容器实例化时创建。
2)prototype模式是在调用getBean()方法时创建。
3)singleton模式可以使用元素的lazy-init="true"属性将对象的创建时机推迟到调用getBean()方法。也可以在(根元素)中使用default-lazy-init="false"推迟所有单例Bean组件的创建时机。见2.6案例中step3、step4。
例如:
2.5为Bean对象执行初始化和销毁方法
1)初始化:可以利用元素的init-method="方法名"属性指定初始化方法。
指定的初始化方法是在构造方法调用后自动执行。若非单例模式,则每创建一个对象,则执行一次初始化方法(单例、非单例模式都可)。见2.6案例中step3、step4。
u 注意事项:
v 初始化的三种方式:写构造方法中;或写{ }中(代码块);Spring框架中元素写init-method="方法名"属性。
v 初始化不能用static{ },它是类加载调用,比创建对象要早。
2)销毁:可以利用元素的destroy-method="方法名"属性执行销毁方法。
指定的销毁方法是在容器关闭时触发,而且只适用于singleton模式的组件(只能为单例模式)。见2.6案例中step3、step4、step6。
2.6案例:Spring框架的使用以及2.1节-2.5节整合测试
step1:导入Spring开发包:spring.jar、commons-logging.jar和配置文件:applicationContext.xml
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd">
Java
public void delete();public void save();
public JdbcCostDAO()
public void myinit()
public void mydestroy()
public void delete()
public void save()
step4:在applicationContext.xml配置文件中,将Bean组件(Java类)交给Spring容器
step5:在org.tarena.test包下,创建TestApplicationContext类,获取Spring容器对象,并测试
@Test
public void test1()
step6:在TestApplicationContext类中添加方法,测试销毁对象
@Test
/**关闭容器才会触发销毁,但关闭容器方法封装在AbstractApplicationContext类中 */
public void test2()
step7:在TestApplicationContext类中添加方法,测试单例
@Test
public void test3()
三、IoC
3.1 IoC概念
1)Inverse of Controller被称为控制反转或反向控制,其实真正体现的是“控制转移”。
2)所谓的控制指的是负责对象关系的指定、对象创建、初始化和销毁等逻辑。
3)IoC指的是将控制逻辑交给第三方框架或容器负责(即把Action中的控制逻辑提出来,交给第三方负责),当两个组件关系发生改变时,只需要修改框架或容器的配置即可。
4)IoC主要解决的是两个组件对象调用问题,可以以低耦合方式建立使用关系。
3.2 DI概念
1)Dependency Injection依赖注入。
2)Spring框架采用DI技术实现了IoC控制思想。
3)Spring提供了两种形式的注入方法:
setter方式注入(常用):依靠set方法,将组件对象传入(可注入多个对象)。
A.首先添加属性变量和set方法。
B.在该组件的定义中采用下面的描述方式:
u 注意事项:例如CostAction中有costDAO属性,而它的标准set方法名为setCostDAO,那么配置文件中的name就应该写costDAO(去掉set,首字母小写)。如果set方法名为setCost,那么name就应该写cost(去掉set,首字母小写)!确切的说,name不是看定义的属性名,而是set方法名。
构造方式注入(用的少):依靠构造方法,将组件对象传入。
A.在需要注入的组件中,添加带参数的构造方法。
B.在该组件的定义中,使用下面格式描述:
Java
3.3案例:测试IoC(set注入)
private CostDAO costDAO;//利用Spring的IOC机制使用CostDAO组件对象,set注入
public void setCostDAO(CostDAO costDAO) { this.costDAO = costDAO; }
public String execute()
step2:在applicationContext.xml配置文件中,将CostAction组件交给Spring容器
@Test//测试set注入
public void test1()
step4:测试结果为:
创建CostDAO对象初始化CostDAO对象 处理资费添加操作
利用JDBC技术实现保存资费记录。
3.4案例:测试IoC(构造注入)
private CostDAO costDAO;
public DeleteAction(CostDAO costDAO)//构造注入
costDAO.delete();//调用CostDAO中的delete方法 return "success";}
step2:在applicationContext.xml配置文件中,将DeleteAction组件交给Spring容器
step3:在TestIoc类中添加方法,测试构造注入
@Test//测试构造注入
public void test2()
step4:测试结果为:
创建CostDAO对象初始化CostDAO对象 处理资费删除操作
利用JDBC技术实现删除资费记录。
3.5案例:不用JDBC访问数据库,而是采用Hibernate访问
接3.3案例,如果不用JDBC访问数据库,而是采用Hibernate访问,则替换组件过程为:
step1:创建HibernateCostDAO类,并实现CostDAO接口
public void delete()
step2:在applicationContext.xml配置文件中,将HibernateCostDAO组件交给Spring容器
step3:修改3.3案例中step2中CostAction组件的描述
step4:再次执行3.3案例step3中test1方法,测试结果为:
处理资费添加操作 利用Hibernate技术实现保存资费记录
Java
四、
Spring中各种类型的数据注入
Spring可以为对象注入以下类型的数据。
4.1 Bean对象注入
4.2基本数据的注入
1)字符串、数字
4.3集合的注入
1)List、Set
集合中的值1集合中的值2…………
2)Map
…………
3)Properties
值1
值2…………
4)特殊用法:set方法接收字符串,内部进行处理(如分割),再存入集合属性
4.4案例:各类数据注入
step1:对象注入参考3.3、3.4案例
step2:创建MessageBean类,并定义不同类型的数据以及对应的set方法
private String username;//用户名private String fileDir;//上传路径
private List hbms;private Set cities;
private Map books;private Properties props;
private Set types;//允许上传类型
/** 注意set方法的名字!不是看属性名,而是看set方法名去掉set,首字母大写的名 */
public void setName(String username) //手动更改过名字
public void setDir(String fileDir) //手动更改过名字
……其他属性名字没改,其他属性代码略
public void setTypes(String str) {//特殊用法:注入一个字符串,分析之后给set集合赋值
String[] arr=str.split(",");types=new HashSet();
for(String s:arr)}
public void show(){
for(String s:hbms)
for(String c:cities)
Set> ens=books.entrySet();
Set keys=props.keySet();//另一种方式遍历
for(Object key:keys)
System.out.println("--允许上传类型如下--");//特殊用法
step3:applicationContext.xml配置文件中
/org/tarena/entity/Cost.hbm.xml
/org/tarena/entity/Admin.hbm.xml
北京上海
true
Java
step4:创建TestInjection类用于测试各类数据的注入
@Test
public void test1(){
String conf="/applicationContext.xml";
ApplicationContext ac=new ClassPathXmlApplicationContext(conf);
MessageBean bean=(MessageBean)ac.getBean("messageBean");
bean.show();}
五、
AOP概念
5.1什么是AOP
Aspect Oriented Programming,被称为面向方面编程。对单个对象(一对一)的解耦用IOC,而当有个共通组件,它对应多个其他组件(一对多),则解耦用AOP。如,拦截器。这也是为何在程序中大量的用IoC,而AOP却用的很少,因为程序中不可能有很多的共通部分。
5.2 AOP和OOP的区别
OOP是面向对象编程,AOP是以OOP为基础的。
OOP主要关注的是对象,如何抽象和封装对象。
AOP主要关注的是方面,方面组件可以以低耦合的方式切入到(作用到)其他某一批目标对象方法中(类似于Struts2中的拦截器)。
AOP主要解决共通处理和目标组件之间解耦。
5.3 AOP相关术语
1)方面(Aspect):指的是封装了共通处理的功能组件。该组件可以作用到某一批目标组件的方法上。
2)连接点(JoinPoint):指的是方面组件和具体的哪一个目标组件的方法有关系。
3)切入点(Pointcut):用于指定目标组件的表达式。指的是方面组件和哪一批目标组件方法有关系。多个连接点组成的集合就是切入点。如:a、b为切入点,1、2为连接点。
4)通知(Advice):用于指定方面组件和目标组件方法之间的作用时机。例如:先执行方面组件再执行目标方法;或先执行目标方法再执行方面组件。
5)目标(Target):利用切入点指定的组件和方法。
6)动态代理(AutoProxy):Spring同样采用了动态代理技术实现了AOP机制。当使用AOP之后,从容器getBean()获取的目标组件,返回的是一个动态生成的代理类。然后通过代理类执行业务方法,代理类负责调用方面组件功能和原目标组件功能。
Spring提供了下面两种动态代理技术实现:
1)采用CGLIB技术实现(目标组件没有接口采用此方法)
例如:public class 代理类 extends 原目标类型 { }
CostAction action=new 代理类();//代理类中有原来类的方法
2)采用JDK Proxy API实现(目标组件有接口采用此方法,即实现了某个接口)
例如:Public class 代理类 implements 原目标接口 { }
CostDAO costDAO=new 代理类();//代理类去实现了原目标接口,所以没有原来类的方法
5.4案例:AOP的使用,模拟某些组件需要记录日志的功能
接3.3、3.4案例,想让所有的操作进行日志记录,那么按以前的方式就需要给所有Action或DAO中添加记录日志的代码,如果Action或DAO很多,那么不便于维护。而使用AOP机制,则可以很方便的实现上述功能:
step1:导入AOP需要的包:aopalliance.jar、aspectjrt.jar、aspectjweaver.jar、cglib-nodep-2.1_3.jar
public void logger()
step3:在applicationContext.xml配置文件中,添加AOP机制
Java
step4:执行3.3案例step3,则发现添加操作已有了记录日志功能
创建CostDAO对象初始化CostDAO对象 记录了用户的操作日志
处理资费添加操作利用JDBC技术实现保存资费记录
step5:执行3.4案例step3,则发现删除操作已有了记录日志功能,记得加无参构造方法!
记录了用户的操作日志处理资费删除操作
利用Hibernate技术实现删除资费记录
u 注意事项:DeleteAction用的是构造注入,所以此处要把无参构造器再加上!因为AOP底层调用了DeleteAction的无参构造方法。不加则报错:Superclass has no null constructors but no arguments were given
5.5通知类型
通知决定方面组件和目标组件作用的关系。主要有以下几种类型通知:
1)前置通知:方面组件在目标方法之前执行。
2)后置通知:方面组件在目标方法之后执行,目标方法没有抛出异常才执行方面组件。
3)最终通知:方面组件在目标方法之后执行,目标方法有没有异常都会执行方面组件。
4)异常通知:方面组件在目标方法抛出异常后才执行。
5)环绕通知:方面组件在目标方法之前和之后执行。
try{ //前置通知执行时机
//执行目标方法
//后置通知执行时机
}catch(Exception e){//异常通知执行时机
}finally{//最终通知执行时机
}//环绕通知等价于前置+后置
5.6切入点
切入点用于指定目标组件和方法,Spring提供了多种表达式写法:
1)方法限定表达式:指定哪些方法启用方面组件。
形式:execution(修饰符? 返回类型 方法名(参数列表) throws 异常?)
示例:
execution(public * * (..)),匹配容器中,所有修饰符是public(不写则是无要求的),返回类型、方法名都没要求,参数列表也不要求的方法。
execution(* set*(..)),匹配容器中,方法以set开头的所有方法。
execution(* org.tarena.CostDAO.*(..)),匹配CostDAO类中的所有方法。
2)类型限定表达式:指定哪些类型的组件的所有方法启用方面组件(默认就是所有方法都启用,且知道类型,不到方法)。
形式:within(类型)示例:
3)Bean名称限定:按元素的id值进行匹配。
形式:Bean(id值)示例:
bean(costDAO),匹配id=costDAO的bean对象。
bean(*DAO),匹配所有id值以DAO结尾的bean对象。
4)args参数限定表达式:按方法参数类型限定匹配。
形式:args(类型)示例:
args(java.io.Serializable),匹配方法只有一个参数,并且类型符合Serializable的方法,public void f1(String s)、public void f2(int i)都能匹配。
u 注意事项:上述表达式可以使用&&、| | 运算符连接使用。
5.7案例:环绕通知,修改5.4案例使之动态显示所执行的操作
step1:新建opt.properties文件,自定义格式:包名.类名.方法名=操作名。在高版本MyEclipse中,切换到Properties界面,点击Add直接输入键和值,则中文会自动转为ASCII码。低版本的则需要使用JDK自带的转换工具:native2ascii.exe
#第一个为资费添加,第二个为资费删除
step2:新建PropertiesUtil工具类,用于解析.properties文件
private static Properties props = new Properties();
staticcatch(Exception ex)}
public static String getValue(String key){
String value = props.getProperty(key);
if(value == null)else}
step3:使用环绕通知,将5.4案例step3中的标签换为
step4:修改5.4案例step2中的LoggerBean类
public Object logger(ProceedingJoinPoint pjp) throws Throwable{//采用环绕通知,加参数
//前置逻辑
String className=pjp.getTarget().getClass().getName();//获取要执行的目标组件类名
String methodName=pjp.getSignature().getName();//获取要执行的方法名
//根据类名和方法名,给用户提示具体操作信息
//解析opt.properties,根据key获取value
String value=PropertiesUtil.getValue(key);
//XXX用户名可以通过ActionContext.getSession获取
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(
new Date(System.currentTimeMillis())));
Object obj=pjp.proceed();//执行目标方法
//后置逻辑
return obj;}
step5:分别执行3.3案例step3和3.4案例step3,执行结果动态显示所执行的操作及时间
XXX执行了资费添加操作!操作时间:2013-08-19 20:14:47
XXX执行了资费删除操作!操作时间:2013-08-19 20:15:45
5.8案例:利用AOP实现异常处理,将异常信息写入文件
1)分析:方面:将异常写入文件。切入点:作用到所有Action业务方法上
public class ExceptionBean {//模拟,将异常信息写入文件
public void exec(Exception ex){//ex代表目标方法抛出的异常
System.out.println("将异常记录文件"+ex);//记录异常信息}}
step2:在applicationContext.xml配置文件中进行配置
step3:在DeleteAction的execute方法中添加异常
String str=null;str.length();
step4:执行3.3案例step3则添加操作执行正常;执行3.4案例step3则删除操作报空指针异常!显示结果:将异常记录文件java.lang.NullPointerException
Java
六、Log4j日志记录工具
6.1 Log4j介绍
Log4j主要用于日志信息的输出。可以将信息分级别(错误、严重、警告、调式信息)按不同方式(控制台、文件、数据库)和格式输出。
Log4j主要有以下3部分组件构成:
1)日志器(Logger):负责消息输出,提供了各种不同级别的输出方法。
2)输出器(Appender):负责控制消息输出的方式,例如输出到控制台、文件输出等。
3)布局器(格式器,Layout):负责控制消息的输出格式。
6.2 Log4j的使用
step1:引入log4j.jar
step2:在src下添加log4j.properties(定义了消息输出级别、采用哪种输出器、采用哪种布局器)
#level:大小写都可,myconsole是自己随便起的appender名字,可以写多个appender
log4j.rootLogger=debug,myconsole,myfile
#appender:可在org.apache.log4j中找自带的类
log4j.appender.myconsole=org.apache.log4j.ConsoleAppender
log4j.appender.myfile=org.apache.log4j.FileAppender
#log4j.appender.myfile.File=D:\error.txt
log4j.appender.myfile.File=D:\error.html
log4j.appender.myconsole.layout=org.apache.log4j.SimpleLayout
log4j.appender.myfile.layout=org.apache.log4j.HTMLLayout
u 注意事项:级别从小到大为:debug、info、warn、error、fatal
step3:创建TestLog4j类,测试利用日志器不同的方法输出消息。
public class TestLog4j {
public static Logger logger=Logger.getLogger(TestLog4j.class);
public static void main(String[] args) {
//能显示就显示,不显示也不会影响主程序后面的运行,仅是个辅助工具
logger.debug("调试信息");logger.info("普通信息");
logger.warn("警告信息");logger.error("错误信息");
logger.fatal("致命信息");}}
u 注意事项:
v 若在log4j.properties中指定的级别为debug,则五种信息都会显示;若指定的级别为error,则只显示error和fatal信息。
6.3案例:修改5.8案例,使用Log4j记录日志
step1:继续使用6.2节step1和step2
step2:修改5.8案例step1
public class ExceptionBean {//将异常信息写入文件
Logger logger=Logger.getLogger(Exception.class);
public void exec(Exception ex){//ex代表目标方法抛出的异常
logger.error("====异常信息====");//记录异常信息
logger.error("异常类型"+ex);
StackTraceElement[] els=ex.getStackTrace();
for(StackTraceElement el:els)}}
step3:执行3.4案例step3则删除操作报空指针异常(前提:已进行了5.8案例step3操作)!由于log4j.properties配置了两种输出方式,所以两种方式都有效。
控制台的显示结果:
XXX执行了资费删除操作!操作时间:2013-08-20 12:47:54
ERROR - ====异常信息====
ERROR - 异常类型java.lang.NullPointerException
……………………
HTML显示结果:
Java
Java学习资料获取(复制下段连接至浏览器即可)
data:text/html;charset=UTF-8;base64,5p625p6E5biI5a2m5Lmg6LWE5paZ5YWN6LS56aKG5Y+W6K+35Yqg5omj5omj5Y+35pivMTAxODkyNTc4MA==
领取专属 10元无门槛券
私享最新 技术干货