你绝对需要的Spring学习笔记(整合部分SSH)
2018-01-12 11:11
483 查看
目 录
一、 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。
<bean id="标识符" class="Bean组件类型"></bean>
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模式,可以通过<bean>的scope属性修改为prototype模式。以后在Web程序中,通过扩展可以使用request、session等值。见2.6案例中step4、step7。
例如:<bean id="标识符" scope="prototype" class="Bean组件类型"></bean>
u 注意事项:对于NetCTOSS项目,一个请求创建一个Action,所以用Spring时必须指明prototype,否则默认使用singleton会出问题。而DAO则可用singleton模式。
2.4 Bean对象创建的时机
1)singleton模式的Bean组件是在容器实例化时创建。
2)prototype模式是在调用getBean()方法时创建。
3)singleton模式可以使用<bean>元素的lazy-init="true"属性将对象的创建时机推迟到调用getBean()方法。也可以在<beans>(根元素)中使用default-lazy-init="false"推迟所有单例Bean组件的创建时机。见2.6案例中step3、step4。
例如:<bean id="标识符" scope="singleton" lazy-init="true" class="Bean组件类型"></bean>
<beans ...... default-lazy-init="false"></beans>
2.5为Bean对象执行初始化和销毁方法
1)初始化:①可以利用<bean>元素的init-method="方法名"属性指定初始化方法。
②指定的初始化方法是在构造方法调用后自动执行。若非单例模式,则每创建一个对象,则执行一次初始化方法(单例、非单例模式都可)。见2.6案例中step3、step4。
u 注意事项:
v 初始化的三种方式:写构造方法中;或写{ }中(代码块);Spring框架中<bean>元素写init-method="方法名"属性。
v 初始化不能用static{ },它是类加载调用,比创建对象要早。
2)销毁:①可以利用<bean>元素的destroy-method="方法名"属性执行销毁方法。
②指定的销毁方法是在容器关闭时触发,而且只适用于singleton模式的组件(只能为单例模式)。见2.6案例中step3、step4、step6。
2.6案例:Spring框架的使用以及2.1节-2.5节整合测试
step1:导入Spring开发包:spring.jar、commons-logging.jar和配置文件: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: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">
</beans>
Java
step2:在org.tarena.dao包下,创建接口CostDAO,添加两个方法
public void delete();public void save();
step3:在org.tarena.dao包下,创建JdbcCostDAO类,并实现CostDAO接口
public JdbcCostDAO(){System.out.println("创建CostDAO对象");}
public void myinit(){System.out.println("初始化CostDAO对象");}
public void mydestroy(){System.out.println("销毁CostDAO对象");}
public void delete() {System.out.println("利用JDBC技术实现删除资费记录");}
public void save() {System.out.println("利用JDBC技术实现保存资费记录");}
step4:在applicationContext.xml配置文件中,将Bean组件(Java类)交给Spring容器
<bean id="jdbcCostDAO" scope="singleton" lazy-init="true" init-method="myinit"
destroy-method="mydestroy"class="org.tarena.dao.JdbcCostDAO">
</bean>
step5:在org.tarena.test包下,创建TestApplicationContext类,获取Spring容器对象,并测试
@Test
public void test1(){String conf="/applicationContext.xml";
ApplicationContext ac=new ClassPathXmlApplicationContext(conf);//实例化容器
CostDAO costDAO=(CostDAO)ac.getBean("jdbcCostDAO");//获取Bean对象
costDAO.save();costDAO.delete();}
step6:在TestApplicationContext类中添加方法,测试销毁对象
@Test
/**关闭容器才会触发销毁,但关闭容器方法封装在AbstractApplicationContext类中 */
public void test2(){String conf="/applicationContext.xml";
ApplicationContext ac=new ClassPathXmlApplicationContext(conf);
AbstractApplicationContext ac=new ClassPathXmlApplicationContext(conf);
CostDAO costDAO=(CostDAO)ac.getBean("jdbcCostDAO");ac.close();}
step7:在TestApplicationContext类中添加方法,测试单例
@Test
public void test3(){String conf="/applicationContext.xml";
ApplicationContext ac=new ClassPathXmlApplicationContext(conf);
CostDAO costDAO1=(CostDAO)ac.getBean("jdbcCostDAO");
CostDAO costDAO2=(CostDAO)ac.getBean("jdbcCostDAO");
System.out.println(costDAO1==costDA
105da
O2);//true,所以Spring默认为单例模式}
三、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.在该组件的<bean>定义中采用下面的描述方式:
<property name="属性名" ref="要注入的Bean对象的id值"></property>
u 注意事项:例如CostAction中有costDAO属性,而它的标准set方法名为setCostDAO,那么配置文件中的name就应该写costDAO(去掉set,首字母小写)。如果set方法名为setCost,那么name就应该写cost(去掉set,首字母小写)!确切的说,name不是看定义的属性名,而是set方法名。
②构造方式注入(用的少):依靠构造方法,将组件对象传入。
A.在需要注入的组件中,添加带参数的构造方法。
B.在该组件的<bean>定义中,使用下面格式描述:
<constructor-arg index="参数索引" ref="要注入的Bean对象的id值"></constructor-arg>
Java
3.3案例:测试IoC(set注入)
step1:接2.6案例,在org.tarena.action包下,创建CostAction类,调用save方法
private CostDAO costDAO;//利用Spring的IOC机制使用CostDAO组件对象,set注入
public void setCostDAO(CostDAO costDAO) { this.costDAO = costDAO; }
public String execute(){System.out.println("处理资费添加操作");
costDAO.save();//调用CostDAO中的save方法return "success";}
step2:在applicationContext.xml配置文件中,将CostAction组件交给Spring容器
<bean id="costAction" scope="prototype" class="org.tarena.action.CostAction">
<!-- 利用setCostDAO方法接收jdbcCostDAO对象 -->
<property name="costDAO" ref="jdbcCostDAO"></property>
<!-- name:与CostAction中对应的set方法匹配的名。ref:指明哪个对象 -->
</bean><!--此处用到了2.6案例中step3描述的组件JdbcCostDAo-->
step3:在org.tarena.test包下,创建TestIoc类,用于测试IOC机制
@Test//测试set注入
public void test1(){String conf="/applicationContext.xml";
ApplicationContext ac=new ClassPathXmlApplicationContext(conf);
CostAction action=(CostAction)ac.getBean("costAction");//获得CostAction类的对象
action.execute();}
step4:测试结果为:
创建CostDAO对象初始化CostDAO对象 处理资费添加操作
利用JDBC技术实现保存资费记录。
3.4案例:测试IoC(构造注入)
step1:接3.3案例,在org.tarena.action包下,创建DeleteAction类,调用delete方法
private CostDAO costDAO;
public DeleteAction(CostDAO costDAO){this.costDAO=costDAO; }//构造注入
public String execute() { System.out.println("处理资费删除操作");
costDAO.delete();//调用CostDAO中的delete方法 return "success";}
step2:在applicationContext.xml配置文件中,将DeleteAction组件交给Spring容器
<bean id="deleteAction" scope="prototype"class="org.tarena.action.DeleteAction">
<!-- 索引0:给构造方法中第一个参数注入一个jdbcCostDAO对象。
若多个参数则重复追加constructor-arg元素即可 -->
<constructor-arg index="0" ref="jdbcCostDAO"></constructor-arg><!-- 构造注入 -->
</bean>
step3:在TestIoc类中添加方法,测试构造注入
@Test//测试构造注入
public void test2(){String conf="/applicationContext.xml";
ApplicationContext ac=new ClassPathXmlApplicationContext(conf);
DeleteAction action=(DeleteAction)ac.getBean("deleteAction");
action.execute();}
step4:测试结果为:
创建CostDAO对象初始化CostDAO对象 处理资费删除操作
利用JDBC技术实现删除资费记录。
3.5案例:不用JDBC访问数据库,而是采用Hibernate访问
接3.3案例,如果不用JDBC访问数据库,而是采用Hibernate访问,则替换组件过程为:
step1:创建HibernateCostDAO类,并实现CostDAO接口
public void delete() {System.out.println("利用Hibernate技术实现删除资费记录");}
public void save() { System.out.println("利用Hibernate技术实现保存资费记录");}
step2:在applicationContext.xml配置文件中,将HibernateCostDAO组件交给Spring容器
<bean id="hibernateCostDAO" class="org.tarena.dao.HibernateCostDAO"></bean>
step3:修改3.3案例中step2中CostAction组件的描述
<bean id="costAction" scope="prototype" class="org.tarena.action.CostAction">
<!-- 修改ref属性的指引 -->
<property name="costDAO" ref="hibernateCostDAO"></property>
<!-- name:与CostAction中添加的属性名相同。ref:指明哪个对象 -->
</bean>
step4:再次执行3.3案例step3中test1方法,测试结果为:
处理资费添加操作 利用Hibernate技术实现保存资费记录
Java
四、
Spring中各种类型的数据注入
Spring可以为对象注入以下类型的数据。
4.1 Bean对象注入
<property name="属性名" ref="要注入的Bean对象的id值"></property>
4.2基本数据的注入
1)字符串、数字
<property name="属性名" value="要注入的值"></property>
4.3集合的注入
1)List、Set
<property name="集合属性名">
<list><!-- 普通值用<value>标签,对象用<bean>标签 -->
<value>集合中的值1</value><value>集合中的值2</value>…………
</list>
</property>
2)Map
<property name="集合属性名">
<map><!-- 普通值用<value>标签,对象用<bean>标签 -->
<entry key="键1" value="值1"></entry>
<entry key="键2" value="值2"></entry> …………
</map>
</property>
3)Properties
<property name="集合属性名">
<props>
<prop key="键1">值1</prop>
<prop key="键2">值2</prop>…………
</props>
</property>
4)特殊用法:set方法接收字符串,内部进行处理(如分割),再存入集合属性
<property name="集合属性名" value="字符串"></property>
4.4案例:各类数据注入
step1:对象注入参考3.3、3.4案例
step2:创建MessageBean类,并定义不同类型的数据以及对应的set方法
private String username;//用户名private String fileDir;//上传路径
private List<String> hbms;private Set<String> cities;
private Map<Integer, String> books;private Properties props;
private Set<String> types;//允许上传类型
/** 注意set方法的名字!不是看属性名,而是看set方法名去掉set,首字母大写的名 */
public void setName(String username) {this.username = username;}//手动更改过名字
public void setDir(String fileDir) {this.fileDir = fileDir;}//手动更改过名字
……其他属性名字没改,其他属性代码略
public void setTypes(String str) {//特殊用法:注入一个字符串,分析之后给set集合赋值
String[] arr=str.split(",");types=new HashSet<String>();
for(String s:arr){types.add(s);}}
public void show(){
System.out.println("用户名:"+username);System.out.println("上传路径:"+fileDir);
System.out.println("--hbms文件如下--");
for(String s:hbms){System.out.println(s);}
System.out.println("--city城市如下--");
for(String c:cities){System.out.println(c);}
System.out.println("--book图书信息--");
Set<Entry<Integer,String>> ens=books.entrySet();
for(Entry en:ens){ System.out.println(en.getKey()+" "+en.getValue()); }
System.out.println("--props参数如下--");
Set keys=props.keySet();//另一种方式遍历
for(Object key:keys){System.out.println(key+" "+
props.getProperty(key.toString()));}
System.out.println("--允许上传类型如下--");//特殊用法
for(String type:types){ System.out.println(type);}}
step3:applicationContext.xml配置文件中
<!-- 各种数据类型的注入 -->
<bean id="messageBean" class="org.tarena.dao.MessageBean">
<!-- 注意名字 name指的是set方法名去掉set,首字母大写的名,不看属性名! -->
<property name="name" value="root"></property><!--手动更改过set方法名 -->
<property name="dir" value="D:\images"></property><!--手动更改过set方法名 -->
<property name="hbms">
<list><value>/org/tarena/entity/Cost.hbm.xml</value>
<value>/org/tarena/entity/Admin.hbm.xml</value></list></property>
<property name="cities">
<set><value>北京</value><value>上海</value></set></property>
<property name="books">
<map><entry key="1" value="Java语言基础"></entry>
<entry key="2" value="Java Web入门"></entry></map></property>
<property name="props">
<props><prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.dialect_sql">org.hibernate.dialect.OracleDialect</prop>
</props>
</property>
<!-- 特殊用法,set方法传入字符串,内部进行处理,再存入集合 -->
<property name="types" value="jpg,gif,jpeg"></property>
</bean>
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
step2:在org.tarena.aop包下新建LoggerBean类,并添加logger方法用于模拟记录日志功能
public void logger(){System.out.println("记录了用户的操作日志");}
step3:在applicationContext.xml配置文件中,添加AOP机制
<bean id="loggerBean" class="org.tarena.aop.LoggerBean"></bean>
<aop:config>
<!--定义切入点,指定目标组件和方法。id:可任意起个名字。expression:指定哪些组件是目标,并作用在这些目标的方法上。下面表示所有Action中的所有方法为切入点-->
<aop:pointcut id="actionPointcut" expression="within(org.tarena.action.*)" />
<!--定义方面,将loggerBean对象指定为方面组件,loggerBean从普通Bean组件升级为了方面组件-->
<aop:aspect id="loggerAspect" ref="loggerBean">
<!-- aop:before在操作前执行 aop:after操作后执行 -->
<!-- 定义通知,aop:before:指定先执行方面组件的logger方法,再执行切入点指定的目标方法。aop:after:与aop:before相反 -->
<aop:before pointcut-ref="actionPointcut" method="logger"/>
</aop:aspect>
</aop:config>
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{ //前置通知执行时机<aop:before>
//执行目标方法
//后置通知执行时机<aop:after-returning>
}catch(Exception e){//异常通知执行时机<aop:after-throwing>
}finally{//最终通知执行时机<aop:after>
}//环绕通知等价于前置+后置<aop:around>
5.6切入点
切入点用于指定目标组件和方法,Spring提供了多种表达式写法:
1)方法限定表达式:指定哪些方法启用方面组件。
①形式:execution(修饰符? 返回类型 方法名(参数列表) throws 异常?)
②示例:
execution(public * * (..)),匹配容器中,所有修饰符是public(不写则是无要求的),返回类型、方法名都没要求,参数列表也不要求的方法。
execution(* set*(..)),匹配容器中,方法以set开头的所有方法。
execution(* org.tarena.CostDAO.*(..)),匹配CostDAO类中的所有方法。
execution(* org.tarena.dao.*.*(..)),匹配dao包下所有类所有方法。
execution(* org.tarena.dao..*.*(..)),匹配dao包及其子包中所有类所有方法。
2)类型限定表达式:指定哪些类型的组件的所有方法启用方面组件(默认就是所有方法都启用,且知道类型,不到方法)。
①形式:within(类型)②示例:
within(com.xyz.service.*),匹配service包下的所有类所有方法
within(com.xyz.service..*),匹配service包及其子包中的所有类所有方法
within(org.tarena.dao.CostDAO),匹配CostDAO所有方法
u 注意事项:within(com.xyz.service..*.*),为错误的,就到方法名!
3)Bean名称限定:按<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
#第一个为资费添加,第二个为资费删除
org.tarena.action.CostAction.execute=资费添加
org.tarena.action.DeleteAction=资费删除
step2:新建PropertiesUtil工具类,用于解析.properties文件
private static Properties props = new Properties();
static{try{props.load(PropertiesUtil.class.getClassLoader()
.getResourceAsStream("opt.properties"));
}catch(Exception ex){ex.printStackTrace();}}
public static String getValue(String key){
String value = props.getProperty(key);
if(value == null){return ""; }else{return value; }}
step3:使用环绕通知,将5.4案例step3中的<aop:before />标签换为<aop:around />
<aop:around pointcut-ref="actionPointcut" method="logger"/>
step4:修改5.4案例step2中的LoggerBean类
public Object logger(ProceedingJoinPoint pjp) throws Throwable{//采用环绕通知,加参数
//前置逻辑
String className=pjp.getTarget().getClass().getName();//获取要执行的目标组件类名
String methodName=pjp.getSignature().getName();//获取要执行的方法名
//根据类名和方法名,给用户提示具体操作信息
String key=className+"."+methodName;System.out.println(key);
//解析opt.properties,根据key获取value
String value=PropertiesUtil.getValue(key);
//XXX用户名可以通过ActionContext.getSession获取
System.out.println("XXX执行了"+value+"操作!操作时间:"+
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业务方法上
within(org.tarena.action..*)。通知:异常通知<aop:after-throwing>。
2)实现:step1:在org.tarena.aop包中创建ExceptionBean类
public class ExceptionBean {//模拟,将异常信息写入文件
public void exec(Exception ex){//ex代表目标方法抛出的异常
System.out.println("将异常记录文件"+ex);//记录异常信息}}
step2:在applicationContext.xml配置文件中进行配置
<bean id="exceptionBean" class="org.tarena.aop.ExceptionBean"></bean>
<aop:pointcut id="actionPointcut" expression="within(org.tarena.action.*)"/>
<!-- 定义方面组件,将exceptionBean指定为方面 -->
<aop:aspect id="exceptionAspect" ref="exceptionBean">
<!-- throwing:和自定的方法中的参数名相同。一定要把异常抛出来才行!
try-catch了则不行! -->
<aop:after-throwing pointcut-ref="actionPointcut" method="exec" throwing="ex"/>
</aop:aspect>
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
#layout:可在org.apache.log4j中找自带的类
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 导包为org.apache.log4j.Logger。
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){logger.error(el);}}}
step3:执行3.4案例step3则删除操作报空指针异常(前提:已进行了5.8案例step3操作)!由于log4j.properties配置了两种输出方式,所以两种方式都有效。
控制台的显示结果:
XXX执行了资费删除操作!操作时间:2013-08-20 12:47:54
ERROR - ====异常信息====
ERROR - 异常类型java.lang.NullPointerException
……………………
HTML显示结果:
Java
以上所述是小编给大家介绍的Spring Boot 模块组成,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的!
Java学习资料(复制下段链接至浏览器即可)
data:textml;charset=UTF-8;base64,5oGt5Zac5L2g77yM5p625p6E5biI5a2m5Lmg576k5Y+35pivNjg2NTc5MDE0Cg==
一、 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。
<bean id="标识符" class="Bean组件类型"></bean>
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模式,可以通过<bean>的scope属性修改为prototype模式。以后在Web程序中,通过扩展可以使用request、session等值。见2.6案例中step4、step7。
例如:<bean id="标识符" scope="prototype" class="Bean组件类型"></bean>
u 注意事项:对于NetCTOSS项目,一个请求创建一个Action,所以用Spring时必须指明prototype,否则默认使用singleton会出问题。而DAO则可用singleton模式。
2.4 Bean对象创建的时机
1)singleton模式的Bean组件是在容器实例化时创建。
2)prototype模式是在调用getBean()方法时创建。
3)singleton模式可以使用<bean>元素的lazy-init="true"属性将对象的创建时机推迟到调用getBean()方法。也可以在<beans>(根元素)中使用default-lazy-init="false"推迟所有单例Bean组件的创建时机。见2.6案例中step3、step4。
例如:<bean id="标识符" scope="singleton" lazy-init="true" class="Bean组件类型"></bean>
<beans ...... default-lazy-init="false"></beans>
2.5为Bean对象执行初始化和销毁方法
1)初始化:①可以利用<bean>元素的init-method="方法名"属性指定初始化方法。
②指定的初始化方法是在构造方法调用后自动执行。若非单例模式,则每创建一个对象,则执行一次初始化方法(单例、非单例模式都可)。见2.6案例中step3、step4。
u 注意事项:
v 初始化的三种方式:写构造方法中;或写{ }中(代码块);Spring框架中<bean>元素写init-method="方法名"属性。
v 初始化不能用static{ },它是类加载调用,比创建对象要早。
2)销毁:①可以利用<bean>元素的destroy-method="方法名"属性执行销毁方法。
②指定的销毁方法是在容器关闭时触发,而且只适用于singleton模式的组件(只能为单例模式)。见2.6案例中step3、step4、step6。
2.6案例:Spring框架的使用以及2.1节-2.5节整合测试
step1:导入Spring开发包:spring.jar、commons-logging.jar和配置文件: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: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">
</beans>
Java
step2:在org.tarena.dao包下,创建接口CostDAO,添加两个方法
public void delete();public void save();
step3:在org.tarena.dao包下,创建JdbcCostDAO类,并实现CostDAO接口
public JdbcCostDAO(){System.out.println("创建CostDAO对象");}
public void myinit(){System.out.println("初始化CostDAO对象");}
public void mydestroy(){System.out.println("销毁CostDAO对象");}
public void delete() {System.out.println("利用JDBC技术实现删除资费记录");}
public void save() {System.out.println("利用JDBC技术实现保存资费记录");}
step4:在applicationContext.xml配置文件中,将Bean组件(Java类)交给Spring容器
<bean id="jdbcCostDAO" scope="singleton" lazy-init="true" init-method="myinit"
destroy-method="mydestroy"class="org.tarena.dao.JdbcCostDAO">
</bean>
step5:在org.tarena.test包下,创建TestApplicationContext类,获取Spring容器对象,并测试
@Test
public void test1(){String conf="/applicationContext.xml";
ApplicationContext ac=new ClassPathXmlApplicationContext(conf);//实例化容器
CostDAO costDAO=(CostDAO)ac.getBean("jdbcCostDAO");//获取Bean对象
costDAO.save();costDAO.delete();}
step6:在TestApplicationContext类中添加方法,测试销毁对象
@Test
/**关闭容器才会触发销毁,但关闭容器方法封装在AbstractApplicationContext类中 */
public void test2(){String conf="/applicationContext.xml";
ApplicationContext ac=new ClassPathXmlApplicationContext(conf);
AbstractApplicationContext ac=new ClassPathXmlApplicationContext(conf);
CostDAO costDAO=(CostDAO)ac.getBean("jdbcCostDAO");ac.close();}
step7:在TestApplicationContext类中添加方法,测试单例
@Test
public void test3(){String conf="/applicationContext.xml";
ApplicationContext ac=new ClassPathXmlApplicationContext(conf);
CostDAO costDAO1=(CostDAO)ac.getBean("jdbcCostDAO");
CostDAO costDAO2=(CostDAO)ac.getBean("jdbcCostDAO");
System.out.println(costDAO1==costDA
105da
O2);//true,所以Spring默认为单例模式}
三、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.在该组件的<bean>定义中采用下面的描述方式:
<property name="属性名" ref="要注入的Bean对象的id值"></property>
u 注意事项:例如CostAction中有costDAO属性,而它的标准set方法名为setCostDAO,那么配置文件中的name就应该写costDAO(去掉set,首字母小写)。如果set方法名为setCost,那么name就应该写cost(去掉set,首字母小写)!确切的说,name不是看定义的属性名,而是set方法名。
②构造方式注入(用的少):依靠构造方法,将组件对象传入。
A.在需要注入的组件中,添加带参数的构造方法。
B.在该组件的<bean>定义中,使用下面格式描述:
<constructor-arg index="参数索引" ref="要注入的Bean对象的id值"></constructor-arg>
Java
3.3案例:测试IoC(set注入)
step1:接2.6案例,在org.tarena.action包下,创建CostAction类,调用save方法
private CostDAO costDAO;//利用Spring的IOC机制使用CostDAO组件对象,set注入
public void setCostDAO(CostDAO costDAO) { this.costDAO = costDAO; }
public String execute(){System.out.println("处理资费添加操作");
costDAO.save();//调用CostDAO中的save方法return "success";}
step2:在applicationContext.xml配置文件中,将CostAction组件交给Spring容器
<bean id="costAction" scope="prototype" class="org.tarena.action.CostAction">
<!-- 利用setCostDAO方法接收jdbcCostDAO对象 -->
<property name="costDAO" ref="jdbcCostDAO"></property>
<!-- name:与CostAction中对应的set方法匹配的名。ref:指明哪个对象 -->
</bean><!--此处用到了2.6案例中step3描述的组件JdbcCostDAo-->
step3:在org.tarena.test包下,创建TestIoc类,用于测试IOC机制
@Test//测试set注入
public void test1(){String conf="/applicationContext.xml";
ApplicationContext ac=new ClassPathXmlApplicationContext(conf);
CostAction action=(CostAction)ac.getBean("costAction");//获得CostAction类的对象
action.execute();}
step4:测试结果为:
创建CostDAO对象初始化CostDAO对象 处理资费添加操作
利用JDBC技术实现保存资费记录。
3.4案例:测试IoC(构造注入)
step1:接3.3案例,在org.tarena.action包下,创建DeleteAction类,调用delete方法
private CostDAO costDAO;
public DeleteAction(CostDAO costDAO){this.costDAO=costDAO; }//构造注入
public String execute() { System.out.println("处理资费删除操作");
costDAO.delete();//调用CostDAO中的delete方法 return "success";}
step2:在applicationContext.xml配置文件中,将DeleteAction组件交给Spring容器
<bean id="deleteAction" scope="prototype"class="org.tarena.action.DeleteAction">
<!-- 索引0:给构造方法中第一个参数注入一个jdbcCostDAO对象。
若多个参数则重复追加constructor-arg元素即可 -->
<constructor-arg index="0" ref="jdbcCostDAO"></constructor-arg><!-- 构造注入 -->
</bean>
step3:在TestIoc类中添加方法,测试构造注入
@Test//测试构造注入
public void test2(){String conf="/applicationContext.xml";
ApplicationContext ac=new ClassPathXmlApplicationContext(conf);
DeleteAction action=(DeleteAction)ac.getBean("deleteAction");
action.execute();}
step4:测试结果为:
创建CostDAO对象初始化CostDAO对象 处理资费删除操作
利用JDBC技术实现删除资费记录。
3.5案例:不用JDBC访问数据库,而是采用Hibernate访问
接3.3案例,如果不用JDBC访问数据库,而是采用Hibernate访问,则替换组件过程为:
step1:创建HibernateCostDAO类,并实现CostDAO接口
public void delete() {System.out.println("利用Hibernate技术实现删除资费记录");}
public void save() { System.out.println("利用Hibernate技术实现保存资费记录");}
step2:在applicationContext.xml配置文件中,将HibernateCostDAO组件交给Spring容器
<bean id="hibernateCostDAO" class="org.tarena.dao.HibernateCostDAO"></bean>
step3:修改3.3案例中step2中CostAction组件的描述
<bean id="costAction" scope="prototype" class="org.tarena.action.CostAction">
<!-- 修改ref属性的指引 -->
<property name="costDAO" ref="hibernateCostDAO"></property>
<!-- name:与CostAction中添加的属性名相同。ref:指明哪个对象 -->
</bean>
step4:再次执行3.3案例step3中test1方法,测试结果为:
处理资费添加操作 利用Hibernate技术实现保存资费记录
Java
四、
Spring中各种类型的数据注入
Spring可以为对象注入以下类型的数据。
4.1 Bean对象注入
<property name="属性名" ref="要注入的Bean对象的id值"></property>
4.2基本数据的注入
1)字符串、数字
<property name="属性名" value="要注入的值"></property>
4.3集合的注入
1)List、Set
<property name="集合属性名">
<list><!-- 普通值用<value>标签,对象用<bean>标签 -->
<value>集合中的值1</value><value>集合中的值2</value>…………
</list>
</property>
2)Map
<property name="集合属性名">
<map><!-- 普通值用<value>标签,对象用<bean>标签 -->
<entry key="键1" value="值1"></entry>
<entry key="键2" value="值2"></entry> …………
</map>
</property>
3)Properties
<property name="集合属性名">
<props>
<prop key="键1">值1</prop>
<prop key="键2">值2</prop>…………
</props>
</property>
4)特殊用法:set方法接收字符串,内部进行处理(如分割),再存入集合属性
<property name="集合属性名" value="字符串"></property>
4.4案例:各类数据注入
step1:对象注入参考3.3、3.4案例
step2:创建MessageBean类,并定义不同类型的数据以及对应的set方法
private String username;//用户名private String fileDir;//上传路径
private List<String> hbms;private Set<String> cities;
private Map<Integer, String> books;private Properties props;
private Set<String> types;//允许上传类型
/** 注意set方法的名字!不是看属性名,而是看set方法名去掉set,首字母大写的名 */
public void setName(String username) {this.username = username;}//手动更改过名字
public void setDir(String fileDir) {this.fileDir = fileDir;}//手动更改过名字
……其他属性名字没改,其他属性代码略
public void setTypes(String str) {//特殊用法:注入一个字符串,分析之后给set集合赋值
String[] arr=str.split(",");types=new HashSet<String>();
for(String s:arr){types.add(s);}}
public void show(){
System.out.println("用户名:"+username);System.out.println("上传路径:"+fileDir);
System.out.println("--hbms文件如下--");
for(String s:hbms){System.out.println(s);}
System.out.println("--city城市如下--");
for(String c:cities){System.out.println(c);}
System.out.println("--book图书信息--");
Set<Entry<Integer,String>> ens=books.entrySet();
for(Entry en:ens){ System.out.println(en.getKey()+" "+en.getValue()); }
System.out.println("--props参数如下--");
Set keys=props.keySet();//另一种方式遍历
for(Object key:keys){System.out.println(key+" "+
props.getProperty(key.toString()));}
System.out.println("--允许上传类型如下--");//特殊用法
for(String type:types){ System.out.println(type);}}
step3:applicationContext.xml配置文件中
<!-- 各种数据类型的注入 -->
<bean id="messageBean" class="org.tarena.dao.MessageBean">
<!-- 注意名字 name指的是set方法名去掉set,首字母大写的名,不看属性名! -->
<property name="name" value="root"></property><!--手动更改过set方法名 -->
<property name="dir" value="D:\images"></property><!--手动更改过set方法名 -->
<property name="hbms">
<list><value>/org/tarena/entity/Cost.hbm.xml</value>
<value>/org/tarena/entity/Admin.hbm.xml</value></list></property>
<property name="cities">
<set><value>北京</value><value>上海</value></set></property>
<property name="books">
<map><entry key="1" value="Java语言基础"></entry>
<entry key="2" value="Java Web入门"></entry></map></property>
<property name="props">
<props><prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.dialect_sql">org.hibernate.dialect.OracleDialect</prop>
</props>
</property>
<!-- 特殊用法,set方法传入字符串,内部进行处理,再存入集合 -->
<property name="types" value="jpg,gif,jpeg"></property>
</bean>
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
step2:在org.tarena.aop包下新建LoggerBean类,并添加logger方法用于模拟记录日志功能
public void logger(){System.out.println("记录了用户的操作日志");}
step3:在applicationContext.xml配置文件中,添加AOP机制
<bean id="loggerBean" class="org.tarena.aop.LoggerBean"></bean>
<aop:config>
<!--定义切入点,指定目标组件和方法。id:可任意起个名字。expression:指定哪些组件是目标,并作用在这些目标的方法上。下面表示所有Action中的所有方法为切入点-->
<aop:pointcut id="actionPointcut" expression="within(org.tarena.action.*)" />
<!--定义方面,将loggerBean对象指定为方面组件,loggerBean从普通Bean组件升级为了方面组件-->
<aop:aspect id="loggerAspect" ref="loggerBean">
<!-- aop:before在操作前执行 aop:after操作后执行 -->
<!-- 定义通知,aop:before:指定先执行方面组件的logger方法,再执行切入点指定的目标方法。aop:after:与aop:before相反 -->
<aop:before pointcut-ref="actionPointcut" method="logger"/>
</aop:aspect>
</aop:config>
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{ //前置通知执行时机<aop:before>
//执行目标方法
//后置通知执行时机<aop:after-returning>
}catch(Exception e){//异常通知执行时机<aop:after-throwing>
}finally{//最终通知执行时机<aop:after>
}//环绕通知等价于前置+后置<aop:around>
5.6切入点
切入点用于指定目标组件和方法,Spring提供了多种表达式写法:
1)方法限定表达式:指定哪些方法启用方面组件。
①形式:execution(修饰符? 返回类型 方法名(参数列表) throws 异常?)
②示例:
execution(public * * (..)),匹配容器中,所有修饰符是public(不写则是无要求的),返回类型、方法名都没要求,参数列表也不要求的方法。
execution(* set*(..)),匹配容器中,方法以set开头的所有方法。
execution(* org.tarena.CostDAO.*(..)),匹配CostDAO类中的所有方法。
execution(* org.tarena.dao.*.*(..)),匹配dao包下所有类所有方法。
execution(* org.tarena.dao..*.*(..)),匹配dao包及其子包中所有类所有方法。
2)类型限定表达式:指定哪些类型的组件的所有方法启用方面组件(默认就是所有方法都启用,且知道类型,不到方法)。
①形式:within(类型)②示例:
within(com.xyz.service.*),匹配service包下的所有类所有方法
within(com.xyz.service..*),匹配service包及其子包中的所有类所有方法
within(org.tarena.dao.CostDAO),匹配CostDAO所有方法
u 注意事项:within(com.xyz.service..*.*),为错误的,就到方法名!
3)Bean名称限定:按<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
#第一个为资费添加,第二个为资费删除
org.tarena.action.CostAction.execute=资费添加
org.tarena.action.DeleteAction=资费删除
step2:新建PropertiesUtil工具类,用于解析.properties文件
private static Properties props = new Properties();
static{try{props.load(PropertiesUtil.class.getClassLoader()
.getResourceAsStream("opt.properties"));
}catch(Exception ex){ex.printStackTrace();}}
public static String getValue(String key){
String value = props.getProperty(key);
if(value == null){return ""; }else{return value; }}
step3:使用环绕通知,将5.4案例step3中的<aop:before />标签换为<aop:around />
<aop:around pointcut-ref="actionPointcut" method="logger"/>
step4:修改5.4案例step2中的LoggerBean类
public Object logger(ProceedingJoinPoint pjp) throws Throwable{//采用环绕通知,加参数
//前置逻辑
String className=pjp.getTarget().getClass().getName();//获取要执行的目标组件类名
String methodName=pjp.getSignature().getName();//获取要执行的方法名
//根据类名和方法名,给用户提示具体操作信息
String key=className+"."+methodName;System.out.println(key);
//解析opt.properties,根据key获取value
String value=PropertiesUtil.getValue(key);
//XXX用户名可以通过ActionContext.getSession获取
System.out.println("XXX执行了"+value+"操作!操作时间:"+
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业务方法上
within(org.tarena.action..*)。通知:异常通知<aop:after-throwing>。
2)实现:step1:在org.tarena.aop包中创建ExceptionBean类
public class ExceptionBean {//模拟,将异常信息写入文件
public void exec(Exception ex){//ex代表目标方法抛出的异常
System.out.println("将异常记录文件"+ex);//记录异常信息}}
step2:在applicationContext.xml配置文件中进行配置
<bean id="exceptionBean" class="org.tarena.aop.ExceptionBean"></bean>
<aop:pointcut id="actionPointcut" expression="within(org.tarena.action.*)"/>
<!-- 定义方面组件,将exceptionBean指定为方面 -->
<aop:aspect id="exceptionAspect" ref="exceptionBean">
<!-- throwing:和自定的方法中的参数名相同。一定要把异常抛出来才行!
try-catch了则不行! -->
<aop:after-throwing pointcut-ref="actionPointcut" method="exec" throwing="ex"/>
</aop:aspect>
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
#layout:可在org.apache.log4j中找自带的类
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 导包为org.apache.log4j.Logger。
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){logger.error(el);}}}
step3:执行3.4案例step3则删除操作报空指针异常(前提:已进行了5.8案例step3操作)!由于log4j.properties配置了两种输出方式,所以两种方式都有效。
控制台的显示结果:
XXX执行了资费删除操作!操作时间:2013-08-20 12:47:54
ERROR - ====异常信息====
ERROR - 异常类型java.lang.NullPointerException
……………………
HTML显示结果:
Java
以上所述是小编给大家介绍的Spring Boot 模块组成,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的!
Java学习资料(复制下段链接至浏览器即可)
data:textml;charset=UTF-8;base64,5oGt5Zac5L2g77yM5p625p6E5biI5a2m5Lmg576k5Y+35pivNjg2NTc5MDE0Cg==
相关文章推荐
- Maven学习笔记(十)-Maven整合SSHSJ(Spring+SpringMVC+Hibernate+Spring Data JPA+JdbcTemplate)框架
- SSH整合学习笔记之spring与hibernate简单整合.doc
- 【SSH学习笔记】整合spring3-hibernate的小项目(一)
- Spring学习笔记--AOP和IOC、SSH整合
- 【SSH学习笔记】整合spring3-hibernate的小项目(二)
- spring学习笔记 -- day12 ssh整合之基于XML的独立式整合
- Spring学习笔记四 整合SSH
- 【SSH学习笔记】整合spring3-struts2的小项目(一)
- 【SSH学习笔记】整合spring3-struts2的小项目(二)
- SSH整合学习笔记之spring与hibernate整合(二)--C3P0
- SSH学习笔记之关于struts-spring-hibernate整合配置问
- 【SSH学习笔记】整合spring3-struts2的小项目(三)
- 【SSH学习笔记】整合spring3-struts2的小项目(四)
- JPA学习笔记-Spring整合JPA
- SSH与SSM学习之SSH整合09——Spring的aop事务
- MongoDB学习13_MongoDB学习笔记之 第4章 MongoDB整合Spring
- 【推荐】springboot学习笔记-6 springboot整合RabbitMQ
- spring boot 学习笔记(3) 整合dubbo
- JavaEE_SSH_Spring学习笔记(9)----让Spring自动扫描和管理Bean
- java 从零开始,学习笔记之基础入门<Struts2_Spring_整合>(四十一)