大致内容
spring的bean管理(注解实现)
AOP原理
log4j介绍
spring整合web项目的演示
一、spring注解实现bean管理
注解:
代码中一些特殊的标记,使用注解也可以完成一些相关的功能(写法"@")
方法上、类上(详见基础加强)
使用注解创建对象,注入属性(完成day01相似的功能)
可以使用注解,但不可能完全替代xml配置文件
准备工作:
导入包:除了day01的6个核心jar包(当然包括日志的包)
再加上aop的那个jar包(注解功能在里面),也就是图中红线的jar包
创建User类和测试方法add():
public class User { public void add(){ System.out.println("注解的:add..."); } }
创建xml文件后需要引入约束:
除了第一天的beans约束;还需要一个用于注解的约束
(也是找html文件夹最后一个文件),引入context的约束
也就是打开xsd-configuration.html后找到这段复制进来
修改后的配置文件如下:
<?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" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 开启注解扫描 ,scan就是扫描之意--> <context:component-scan base-package="cn.anotation"></context:component-scan> <!-- 只扫描属性上的注解,用的少 --> <!-- <context:annotation-config></context:annotation-config> --> </beans>
准备工作准备完毕后,可以开始注解开发:
1.注解创建对象:
在要创建的对象的类上加注解:
用 @Component(value="user")注解
虽然目前这3 个注释和 @Component 相比没有什么新意,但 Spring 将在以后的版本中为它们添加特殊的功能。
所以,如果 Web 应用程序采用了经典的三层分层结构的话,最好在持久层、业务层和控制层分别采用上述注解对分层中的类进行注释。
@Service 用于标注业务层组件 ===业务层
@Controller 用于标注控制层组件(如struts中的action)===WEB层
@Repository 用于标注数据访问组件,即DAO组件 ===持久层
@Component 泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
可以加个注解,控制是单实例或者多实例
在类上多加一行注解(内容参考昨天的,singleton但,prototype)
@Scope(value="prototype") //设置是单实例还是多实例
注解配置后的User类如下:
package cn.anotation; import org.springframework.stereotype.Component; /*@Scope(value="prototype") //设置是单实例还是多实例*/ @Component(value="user") //相当于原来的<bean id=use>r的配置 public class User { public void add(){ System.out.println("注解的:add..."); } }
2.注解注入属性:
还是使用service中注入dao对象(对比day01的xml的配置实现)
对比xml实现步骤:还是需要先创建对象,再实现注入
先在两个类上加类注解
再在service中加dao成员变量dao,给成员变量加注解而无需set()方法:
主要有两个属性装配的注解:
@Autowried :自动注入找到对象的方式是根据类名找的对象进行注入
(与dao的类注解的value值无关),比较不直观,没有指定对象
【注解更清晰】@Resource(name="要注入的对象名(dao的类注解的value值)")
若与value值不一样会报错(找错比较快的方式是找Casue By这一行,错误最准确)
注解配置完成后的service和dao类如下:
package cn.anotation; import org.springframework.stereotype.Component; @Component(value="userDao") public class UserDao { public void add(){ System.out.println("dao.add()"); } }
package cn.anotation; import javax.annotation.Resource; import org.springframework.stereotype.Service; @Service(value="userService") public class UserService { //定义成员属性,无需set()方法,直接对属性加注解即可 //@Autowired @Resource(name="userDao") private UserDao dao; public void add(){ System.out.println("service.add()"); dao.add(); } }
做个简单的测试如下:(请勿将测试类名命名为Test)
package cn.anotation; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test01 { //注解创建对象 @Test public void test(){ //加载spring核心配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); //得到配置创建的对象 User user =(User)context.getBean("user"); user.add(); } //注解注入属性 @Test public void test02(){ //加载spring核心配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); //得到配置创建的对象 UserService service =(UserService)context.getBean("userService"); service.add(); } }
配置文件和注解混合使用:
一般创建对象使用配置文件
属性注入使用注解的方式
案例cn.xmlandano包下,使用bean2.xml配置文件
步骤也是建立类,创建对象使用xml,而属性注入使用注解
采用service中注入两个dao,三个类和配置文件如下:
BookDao:
package cn.xmlandano; public class BookDao { public void buy(){ System.out.println("dao.buy()"); } }
OrderDao:
package cn.xmlandano; public class OrderDao { public void buy(){ System.out.println("order.buy()"); } }
BookService:
package cn.xmlandano; import javax.annotation.Resource; public class BookService { //使用注解注入属性 @Resource(name="bookDao") private BookDao bookDao; @Resource(name="oderDao") private OrderDao orderDao; public void buy(){ System.out.println("service.buy()"); bookDao.buy(); orderDao.buy(); } }
配置文件:
<?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" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 开启注解扫描 ,scan就是扫描之意--> <context:component-scan base-package="cn"></context:component-scan> <!-- 创建对象 --> <bean id="bookService" class="cn.xmlandano.BookService"></bean> <bean id="bookDao" class="cn.xmlandano.BookDao"></bean> <bean id="oderDao" class="cn.xmlandano.OrderDao"></bean> </beans>
测试类与上一个测试类类似,不再赘述。
二、AOP
1)AOP概述
2)AOP底层原理
3)AOP相关术语
4)AOP操作
1)AOP概述:面向切面编程
struts2中的第一个浅的层面:不修改源代码拓展功能
AOP采取横向抽取机制,取代传统的纵向继承体系的重复性代码
2)AOP底层原理:
例如有一个这样的Service的类和add()方法
public class UserService(){ public void add(){ //添加用户逻辑 } }
现在想要为add()方法添加日志功能,需要在add()方法内部加入实现代码;
如果有很多方法(update.delete...)等很多方法都要重复性加,不可取!
第一阶段的纵向的解决方案是新建一个类UserLog 定义实现log()日志方法;
其它类想要实现采用继承UserLog类
UserService extends UserLog
于是可以调用父类的方法实现日志功能:super.log();
缺陷是例如方法名称发生了变化,则所有的子类的调用代码都要修改
AOP解决方案是横向抽取机制(底层动态代理);
动态代理做的主要的事情是进行方法的增强;
第一种情况:有接口的情况,使用jdk动态代理
使用动态代理创建接口实现类和代理对象,例如有一个interface Dao 和 class DaoImpl implements Dao
创建一个和DaoImpl平级的对象,但是这个不是真正的对象而是一个代理对象,但代理对象和原对象有相同的功能
第二种情况:没有接口的情况,cglib动态代理
创建User类的子类的代理对象,在子类中可以调用父类的方法完成增强
3)AOP相关术语(重点掌握的为带*的)
Joinpoint:连接点
类里面哪些方法可以被增强,这些方法就称为连接点
*Pointcut:切入点
在类中可以有很多方法被增强,在实际操作中只增强了部分方法,实际增强的方法就称为切入点
*advice:通知/增强
例如之前想要加log()日志功能,这个日志功能就叫增强,也就是实际拓展的功能的逻辑
通知/增强分为:例如要增强add()方法
前置通知:在方法之前
后置通知:在方法之后
异常通知:在出现异常后通知(用的少)
最终通知:在后置之后执行
环绕通知:方法前、后都要执行(例如计算方法执行时间)
*Aspect:切面
把增强应用到切入点的过程,这个过程就叫切面。例如在add()方法上增强一个日志功能
剩下几个作了解:
引介,目标对象,织入(把增强应用到类的过程),代理
4)spring的AOP操作(达到会用):
使用AspectJ进行AOP操作,AspectJ是一个面向切面的框架,
经常被用来和spring一起使用来进行AOP的操作,虽然它本身不是spring的一部分
使用aspectJ实现AOP操作主要有两种方式:
1.基于aspectJ的xml配置
2.基于aspectJ的注解实现(day03补充)
AOP操作准备工作:
导入AOP相关的jar包
aop asp.. spring-asp spring-aop详见spring02项目(此时有如图10个jar包了)
创建核心配置文件,导入AOP的约束(bean3.xml)找约束不再赘述
实际操作:
建两个类Book 增强的类 BuyBook
package cn.aop; //AOP操作 public class Book { public void buy(){ System.out.println("买书"); } }
package cn.aop; import org.aspectj.lang.ProceedingJoinPoint; //增强book类 public class BuyBook { public void before1(){ System.out.println("买书前"); } //环绕通知可以使用一个参数 public void around(ProceedingJoinPoint pj) throws Throwable{ System.out.println("方法之前-环绕"); //执行被增强的方法 pj.proceed(); System.out.println("方法之后-环绕"); } }
使用表达式配置来配置切入点(增强的方法)
bean3.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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 配置对象 --> <bean id="book" class="cn.aop.Book"></bean> <bean id="buyBook" class="cn.aop.BuyBook"></bean> <!-- 配置aop操作 --> <aop:config> <!-- 1.配置切入点,express为配置表达式 id为切入点的名字 --> <aop:pointcut expression="execution(* cn.aop.Book.buy(..))" id="pointcut1"/> <!-- 2.配置切面(增强的过程) ref属性为增强的对象--> <aop:aspect ref="buyBook"> <!-- 前置增强 ,method指定增强类的哪个方法作为前置增强,pointcut-ref为要增强的切入点--> <aop:before method="before1" pointcut-ref="pointcut1"/> <!-- 环绕通知 --> <aop:around method="around" pointcut-ref="pointcut1"/> </aop:aspect> </aop:config> </beans>
常用表达式:
格式:execution(<访问修饰符>?<返回值><方法名>(参数)<异常>)
一般 访问修饰符写 *
后面接方法的全路径
例如:注意的点:可以使用通配符, *后加空格以示区分
注意参数里是..两个点,表示参数包含在里面
execution(* cn.aop.Book.add(..))
execution(* cn.aop.Book.*)
匹配所有以save开头的方法
execution(* save*(..))
具体配置及解释见bean3.xml
配置略显麻烦,后期引入注解形式!
后置通知类似不再赘述
环绕通知:
比较特别的是增强方法的写法(注意一个参数),配置文件同理
作个简单测试:
package cn.aop; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test03 { @Test public void test(){ //加载spring核心配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml"); //得到配置创建的对象 Book book =(Book)context.getBean("book"); book.buy(); } }
三、log4j介绍
(之前一直有警告)
通过log4j可以看到程序运行过程中更详尽的信息
(哪些对象被创建了,什么配置文件被修改了以及一些错误信息等等)
使用的步骤:
导包(之前已导)
复制log4j的配置到src下(只需看懂,无需手写)
之前没加配置文件它就不知道该以什么样的格式输出
rootLogger 日志级别
INFO 基本信息
DEBUG 详细信息
//log4j待补充
这里贴出一个log4j.properties简单示例(注意修改一些例如路径等参数):
# Set root logger level to WARN and append to stdout log4j.rootLogger=WARN, stdout, error #WARN\u4E3Alog\u8F93\u51FA\u7EA7\u522B\uFF0Cstdout\uFF0Cerror\u4E3A\u8BE5log\u7684\u522B\u540D\uFF0C\u4E0B\u9762\u5C06\u7528\u5230 log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout # Pattern to output the caller‘s file name and line number. log4j.appender.stdout.layout.ConversionPattern=%d %5p (%c:%L) - %m%n # Print only messages of level ERROR or above in the package noModule. log4j.logger.noModule=FATAL # OpenSymphony Stuff log4j.logger.com.opensymphony=INFO log4j.logger.com.opensymphony.webwork=DEBUG # Spring Stuff log4j.logger.org.springframework=INFO ################################# # \u9519\u8BEF\u4FE1\u606F # ################################# log4j.appender.error=org.apache.log4j.DailyRollingFileAppender log4j.appender.error.File=F:/errors.log log4j.appender.error.layout=org.apache.log4j.PatternLayout log4j.appender.error.layout.ConversionPattern=[%d]-%-5p (%F:%L)|%m%n log4j.appender.error.DatePattern=‘.‘yyyy-MM-dd log4j.appender.error.Threshold=ERROR ################################### # CONSOLE # ################################# log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=(%F:%L)|%m%n log4j.appender.errorcsle=org.apache.log4j.ConsoleAppender log4j.appender.errorcsle.layout=org.apache.log4j.PatternLayout log4j.appender.errorcsle.layout.ConversionPattern=%-5p (%F:%L)|%m%n log4j.appender.errorcsle.Threshold=ERROR ################################## # \u4E1A\u52A1\u7CFB\u7EDF # ################################# log4j.logger.cn.vesung=DEBUG, logic log4j.appender.logic=org.apache.log4j.DailyRollingFileAppender log4j.appender.logic.File=F:/logic.log log4j.appender.logic.layout=org.apache.log4j.PatternLayout log4j.appender.logic.layout.ConversionPattern=[%d]-%-5p (%F:%L)|%m%n log4j.appender.logic.DatePattern=‘.‘yyyy-MM-dd
四、Spring整合web项目
在action里调service,service里调dao;
service里调dao可以用属性注入;
在spring02_web里演示
导包:
先导入struts2和spring的jar包
建立action(继承ActionSupport) service(依赖注入dao) dao
在action中测试:action在struts.xml中配置(一定要配置过滤器!!!,不然404)
目录结构如下:
UserDao:
package cn.dao; public class UserDao { public void add(){ System.out.println("dao.add"); } }
UserService:
package cn.service; import cn.dao.UserDao; public class UserService { private UserDao dao; public void setDao(UserDao dao) { this.dao = dao; } public void add(){ System.out.println("service.add"); dao.add(); } }
UserAction:
package cn.action; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.opensymphony.xwork2.ActionSupport; import cn.service.UserService; public class UserAction extends ActionSupport{ @Override public String execute() throws Exception { //在action中测试 ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); UserService service = (UserService) context.getBean("userService"); service.add(); return NONE; } }
struts.xml配置如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <package name="demo01" extends="struts-default" namespace="/"> <action name="userAction" class="cn.action.UserAction"> </action> </package> </struts>
spring的bean1.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-4.3.xsd"> <!-- 创建service --> <bean id="userService" class="cn.service.UserService"> <!-- 注入dao --> <property name="dao" ref="dao"></property> </bean> <!-- 创建dao --> <bean id="dao" class="cn.dao.UserDao"></bean> </beans>
会出现一个小问题,通过log4j可以看到,每次访问都要 创建对象加载文件等
可以通过在服务器启动时就加载完成,把服务器给压力
整合的基本原理第一天已有介绍这里直接贴过来:
这个问题spring已经给我们封装好了
===spring整合web项目
Hibernate时有一个遗留问题:sessionFactory的创建会比较慢,可以交给服务器来创建
还有上面的bean1.xml每次都要加载spring核心配置文件,会影响性能
以上的类似问题,解决的实现思想都是:
把加载配置文件和创建对象在服务器启动时就创建,把压力给服务器
spring中封装了相关的处理类,这里简单介绍原理:
web阶段中有一个与天地同寿的对象 ServletContext
它的创建可以由监听器进行监听
在服务器启动的时候,服务器会为每个项目创建一个独一无二的对象:ServletContext
可以使用监听器对ServletContext进行监听,可以知道对象的创建时间
于是可以在监听器监听到ServletContext创建后
加载spring配置文件,把配置文件中配置的对象进行创建
对象创建后将创建的对象放在ServletContext中(它也是一个域对象)
域对象的存取数据直接使用get/setAttribute()即可
一个监听器,只需要配置这个监听器即可
在web.xml中,使用listener配置:
要整合web项目,还要一些整合的jar包:spring-web的包
<!-- 配置监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
这里需要指定加载spring配置文件的位置
(不然会去默认的路径下找默认的文件)WEB-INF/applicatonContext.xml
<!-- 指定spring配置文件的位置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:bean1.xml</param-value>
</context-param>
这个参数名称去监听器的父类的常量中找
这里贴出web.xml的配置文件:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>spring02_web</display-name> <!-- 指定spring配置文件的位置 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:bean1.xml</param-value> </context-param> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 配置监听器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> </web-app>