JavaWeb框架-【Spring】-2-让你我痴迷滴Spring!
2017-11-06 10:48
267 查看
话说:
各位读者早上好,上一篇博客初步了解了下Spring,这篇深入总结下。
Hibernate、Struts2现在主流用得少了,但是它们蕴含的思想还是蛮好的;Spring则非常强大,如春天般生机盎然。主流的SSM开发框架中的springMVC及MyBatis将在后续几篇博客中“粉墨登场”。到现在为止SSH的每个成员就介绍完毕了,下一篇博客将整合Spring+Struts2+Hibernate。
还是那句话,技术会过时,但是其中孕育的思想不会过时。
开发环境
IntelliJ IDEA(2017.2.5)
目录:
一、构造方法注入
二、自动装配
三、注解
四、设计模式
五、三种方式实现AOP
六、总结
一、构造方法注入
整体布局如下:
这次引入接口,通过面向接口编程的方式,来实现构造方法注入。类中以接口作为属性,然后配置bean,接着配置bean的属性。属性如果不注入,就会报错:NullPointExcepiton
前期准备:
新建IUserDao、UserDa
4000
o负责数据持久化;IUserService、UserService业务逻辑处理。
IUserDao
UserDao
IUserService
UserService
我们的目的是在UserService里面要调用IUserDao中的方法,本质就是调用UserJdbcDao中的方法。不过这里属性我们定义为接口,而不是直接是对应Dao层类。什么用呢?方便扩展。下次再增加IBookDao、BookDao就很方便。
上一篇博客我们是怎么注入的?我们定义了属性IUserDao,然后生成get(),set()方法,在spring.xml中配置UserService的bean,然后给这个bean配置property,属性名字要和set()后的setYY 中的yy保持一致。
这里要实现的目标是,通过构造方法属性注入,就不用生成set()了。如何实现构造方法注入?
第一步:UserService中生成构造方法
第二步:spring.xml中配置
无非就是换一种方式注入属性而已。需要注意的是,这里如果没有配置,就会报错:NullPointException,因为找不到具体属性值呢。
二、自动装配
自动装配就是通过一个属性Autowired来配置属性,替换之前需要配置
property这个属性节点。
主要类型:no、 byName、byType、constructor
1) byName
顾名思义,byName,属性名不可以乱取;定义属性名字要和要引用的bean一致。
第一步:定义属性并生成get() set()
第二步:自动装配 加上autowire=”byName”
注意:这里因为是byName,定义的属性userJdbcDao要和bean的name一致,否则就报错NullPointExcep,找不到嘛!
2) byType
顾名思义,根据类型。什么类型,你定义的属性,要注入的属性的类型。所以名字随便取。
第一步:定义接口属性,并生成get set
第二步:自动装配 加入:autowire=”byType”
But!
弊端:如果这个接口有多个类实现,就会找不到具体调用哪个类的方法了。会报错:
所以,谨慎使用。用的时候,属性就不要定义为接口了,直接定义为对应的实现类。
3) constructor
构造方法,顾名思义,之前的构造方法属性注入也可以通过autowire=”constructor”来配置
第一步:定义接口类型属性和构造方法
第二步:加入自动装配constructor
But!
和byType一样,如果有多个类实现同一个接口,那么系统就不知道该调用实现类的方法
就会报错:java.lang.NullPointerException;比如以下两个bean都实现了IUserDao这个接口的add()方法
三、注解
什么用呢?之前我们每次都要在spring.xml中配置bean,就代表类实例化了,这样有木有觉得很麻烦?是的,确实如此。怎么简化呢?注解。注解大家很熟悉,Junit中的@Test从此解放了main方法,测试起来灰常方便了呢。
1)主要几个注解@Repository @Service @Controller @Autowired @Qualifier 等
如何实现注解呢?
(1)开启注解支持
官方文档在spring.xml中加入头部信息
(2) 扫描加入注解的包
(3)给相应业务类加上相应注解
如果生成了get() set()方法,就通过@Resource方法注解,等同于@Autowired + @Qualifier
注意:@Service 和@Repository默认就是类名首字母小写,如果重命名的话,引用的时候,也要用引用的名字,否则报错:
No bean named ‘userJdbcDao’ available
2)自定义注解
我们不能总做使用者吧?如果可能,我们也要尽可能去创造。
1、新建一个注解类
2、加入元注解
3、测试
四、设计模式
就是把所有的代码结构化分层,每一层职责明确。不论项目多么复杂,项目结构清晰明了。Model层
就是JavaBean,业务类的实体类;Dao层负责数据持久化,和数据库接入;Service层调用Dao层方法,实现
复杂的业务逻辑处理,Controller层主要做三件事:处理页面请求并接收参数、调用Service层方法、页面跳转。
各层层级关系明朗:Controller==>Service==>Dao 上层依赖于下层,下层为上层提供服务。
其实《金字塔结构》这本书讲解的是平时说话做事的逻辑层级;在代码世界里,本质也差不多。
五、三种方式实现AOP
1)经典方式:静态代理与动态代理
2)注解方式
3)XML配置方式
AOP——Aspect-Oriented Programming 面向切面编程
OC原则(open close);在不改编源码的情况下,为原代码织入新功能。
底层依赖的JDK的反射机制。反射我们不陌生了,之前JDBC一些列中,查的方法最后用到了Field.
1)经典方式:静态代理与动态代理
a、静态代理如何实现?
b、动态代理?
目的:UserService在调用Dao层add()方法时,为其增加日志信息。日志信息内容如下:
a、静态代理如何实现?
b、动态代理?
先单纯实现动态代理,不与spring整合。
通过spring.xml来实现动态代理
首先编写一个类,类似上面的。
配置spring.xml
弊端:无法实现指定方法输出日志。
一般,查询方法不加日志,但是增修、修改、删除加日志,查询方法不加日志。如何处理?
做个判断:
如果方法名不一致呢?加入多个判断?
//对带有指定参数args的指定对象target调用由此 Method 对象表示的底层方法
你能穷尽所有查的方法么?只要名字一变化,就不一样了…所以注解最方便:
2)注解方式实现AOP
@AspectJ-简化配置
有木有觉!得上面的动态代理很麻烦,比较复杂?其实笔者测试的时候,不甚顺利。注解方式实现AOP真的是So Easy!
(1)导入包aspectj包
只有导入这个包,才能用@AspectJ这个注解,不然没有呢
(2)开启spring AOP支持
要想开启支持,就需要有这个标签可以用,头部只有加入spring-aop,才会有下面的标签。
(3)定义切面
在代理类头部只加上@Aspect是不够的,还需要加上@Component
* @Aspect表明这是个切面类,里面会定义切入点。
@Component表明这个类在bean中所处的位置
* 因为它不属于Repository\Service\Controller
(4)定义通知
3)XML配置方式实现AOP
注解有注解的好处,但是过多注解,也让人不知所云,而且注解最大弊端就是侵入性,直接改源代码去了,这时候配置就出场了。大型项目很多用配置,便于修改,结构清晰。
1、开启AOP支持
各位读者早上好,上一篇博客初步了解了下Spring,这篇深入总结下。
Hibernate、Struts2现在主流用得少了,但是它们蕴含的思想还是蛮好的;Spring则非常强大,如春天般生机盎然。主流的SSM开发框架中的springMVC及MyBatis将在后续几篇博客中“粉墨登场”。到现在为止SSH的每个成员就介绍完毕了,下一篇博客将整合Spring+Struts2+Hibernate。
还是那句话,技术会过时,但是其中孕育的思想不会过时。
开发环境
IntelliJ IDEA(2017.2.5)
目录:
一、构造方法注入
二、自动装配
三、注解
四、设计模式
五、三种方式实现AOP
六、总结
一、构造方法注入
整体布局如下:
这次引入接口,通过面向接口编程的方式,来实现构造方法注入。类中以接口作为属性,然后配置bean,接着配置bean的属性。属性如果不注入,就会报错:NullPointExcepiton
前期准备:
新建IUserDao、UserDa
4000
o负责数据持久化;IUserService、UserService业务逻辑处理。
IUserDao
package com.hmc.spring.dao; public interface IUserDao { void add(); }
UserDao
package com.hmc.spring.dao; /** * User:Meice * 2017/11/4 */ public class UserJdbcDao implements IUserDao { public void add() { System.out.println("使用JDBC方式保存用户...."); } }
IUserService
package com.hmc.spring.service; public interface IUserService { void save(); }
UserService
package com.hmc.spring.service; import com.hmc.spring.dao.IUserDao; /** * User:Meice * 2017/11/4 */ public class UserService implements IUserService { // private IUserDao userJdbcDao; private IUserDao userDao; /*public IUserDao getUserJdbcDao() { return userJdbcDao; } public void setUserJdbcDao(IUserDao userJdbcDao) { this.userJdbcDao = userJdbcDao; }*/ //最好一同也写一个无参的,避免忘记带参构造方法属性注入时报错:No default constructor found; public UserService() {} //构造方法初始化 public UserService(IUserDao userDao) { this.userDao = userDao; } public void save() { //这里面要调用JDBC的add方法 userDao.add(); //自动装配autowire="byName" //userJdbcDao.add(); } /* public IUserDao getUserDao() { return userDao; } public void setUserDao(IUserDao userDao) { this.userDao = userDao; } */ }
我们的目的是在UserService里面要调用IUserDao中的方法,本质就是调用UserJdbcDao中的方法。不过这里属性我们定义为接口,而不是直接是对应Dao层类。什么用呢?方便扩展。下次再增加IBookDao、BookDao就很方便。
上一篇博客我们是怎么注入的?我们定义了属性IUserDao,然后生成get(),set()方法,在spring.xml中配置UserService的bean,然后给这个bean配置property,属性名字要和set()后的setYY 中的yy保持一致。
这里要实现的目标是,通过构造方法属性注入,就不用生成set()了。如何实现构造方法注入?
第一步:UserService中生成构造方法
package com.hmc.spring.service; import com.hmc.spring.dao.IUserDao; /** * User:Meice * 2017/11/4 */ public class UserService implements IUserService { private IUserDao userDao; //最好一同也写一个无参的,避免忘记带参构造方法属性注入时报错:No default constructor found; public UserService() {} //构造方法初始化 public UserService(IUserDao userDao) { this.userDao = userDao; } public void save() { //这里面要调用JDBC的add方法 userDao.add(); //自动装配autowire="byName" //userJdbcDao.add(); } }
第二步:spring.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"> <!--注入userJdbcDao--> <bean name="userJdbcDao" class="sdcom.hmc.spring.dao.UserJdbcDao"></bean> <!-- <bean name="userHibernateDao" class="com.hmc.spring.dao.UserHibernateDao"></bean>--> <!--注入userService--> <bean name="userService" class="com.hmc.spring.service.UserService" autowire="constructor"> <!--byName表示根据bean的名字来自动装配,所以引用的时候,属性名要和bean名字一致并生成get(),set() 而且,给哪个bean加上自动装配,表示对其引用属性进行自动装配,而不是其本身--> <!--属性注入--> <!--<property name="userDao" ref="userJdbcDao"></property>--> <!--构造方法注入--> <constructor-arg index="0" ref="userJdbcDao"></constructor-arg> </bean> </beans>
无非就是换一种方式注入属性而已。需要注意的是,这里如果没有配置,就会报错:NullPointException,因为找不到具体属性值呢。
二、自动装配
自动装配就是通过一个属性Autowired来配置属性,替换之前需要配置
property这个属性节点。
主要类型:no、 byName、byType、constructor
1) byName
顾名思义,byName,属性名不可以乱取;定义属性名字要和要引用的bean一致。
第一步:定义属性并生成get() set()
private IUserDao userJdbcDao; public IUserDao getUserJdbcDao() { return userJdbcDao; } public void setUserJdbcDao(IUserDao userJdbcDao) { this.userJdbcDao = userJdbcDao; }
第二步:自动装配 加上autowire=”byName”
<bean name="userJdbcDao" class="com.hmc.spring.dao.UserJdbcDao"></bean> <bean name="userService" class="com.hmc.spring.service.UserService" autowire="byType"> //这里神马也不用写奥~~~ </bean>
注意:这里因为是byName,定义的属性userJdbcDao要和bean的name一致,否则就报错NullPointExcep,找不到嘛!
2) byType
顾名思义,根据类型。什么类型,你定义的属性,要注入的属性的类型。所以名字随便取。
第一步:定义接口属性,并生成get set
private IUserDao userDao; public IUserDao getUserDao() { return userDao; } public void setUserDao(IUserDao userDao) { this.userDao = userDao; }
第二步:自动装配 加入:autowire=”byType”
<bean name="userJdbcDao" class="com.hmc.spring.dao.UserJdbcDao"></bean> <bean name="userService" class="com.hmc.spring.service.UserService" autowire="byType"> //这里神马也不用写 </bean>
But!
弊端:如果这个接口有多个类实现,就会找不到具体调用哪个类的方法了。会报错:
java.lang.ExceptionInInitializerError Caused by: ....NoUniqueBeanDefinitionException: No qualifying bean of type 'com.hmc.spring.dao.IUserDao' available: expected single matching bean but found 2: userJdbcDao,userHibernateDao
所以,谨慎使用。用的时候,属性就不要定义为接口了,直接定义为对应的实现类。
3) constructor
构造方法,顾名思义,之前的构造方法属性注入也可以通过autowire=”constructor”来配置
第一步:定义接口类型属性和构造方法
private IUserDao userDao; public UserService() {} //构造方法初始化 public UserService(IUserDao userDao) { this.userDao = userDao; }
第二步:加入自动装配constructor
第二步:加入自动装配constructor <bean name="userJdbcDao" class="com.hmc.spring.dao.UserJdbcDao"></bean> <!--注入userService--> <bean name="userService" class="com.hmc.spring.service.UserService" autowire="constructor"> //这里神马也不用写! </bean>
But!
和byType一样,如果有多个类实现同一个接口,那么系统就不知道该调用实现类的方法
就会报错:java.lang.NullPointerException;比如以下两个bean都实现了IUserDao这个接口的add()方法
<bean name="userJdbcDao" class="com.hmc.spring.dao.UserJdbcDao"></bean> <bean name="userHibernateDao" class="com.hmc.spring.dao.UserHibernateDao"></bean> <!--注入userService--> <bean name="userService" class="com.hmc.spring.service.UserService" autowire="constructor"> </bean>
三、注解
什么用呢?之前我们每次都要在spring.xml中配置bean,就代表类实例化了,这样有木有觉得很麻烦?是的,确实如此。怎么简化呢?注解。注解大家很熟悉,Junit中的@Test从此解放了main方法,测试起来灰常方便了呢。
1)主要几个注解@Repository @Service @Controller @Autowired @Qualifier 等
如何实现注解呢?
(1)开启注解支持
官方文档在spring.xml中加入头部信息
xmlns:context="http://www.springframework.org/schema/context" http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
(2) 扫描加入注解的包
<context:component-scan base-package="com.hmc.spring"/>
(3)给相应业务类加上相应注解
Dao层 @Repository public class UserDao implements IUserDao {} Service层 @Service public class UserService implements IUserService {} Autowired实现自动装配 @Autowired(默认byType) private IUserDao userDao; 可以使用Qualfier指定bean的名称 @Autowired @Qualifier(value = "userJdbcDao")——手动指定在哪个bean进行装配 private IUserDao userDao;
如果生成了get() set()方法,就通过@Resource方法注解,等同于@Autowired + @Qualifier
private IUserDao userDao; public IUserDao getUserDao() { return userDao; } @Resource(name = "userHibernateDao")//生成set()方法,通过Resource( name=""指定属性) public void setUserDao(IUserDao userDao) { this.userDao = userDao; }
注意:@Service 和@Repository默认就是类名首字母小写,如果重命名的话,引用的时候,也要用引用的名字,否则报错:
No bean named ‘userJdbcDao’ available
2)自定义注解
我们不能总做使用者吧?如果可能,我们也要尽可能去创造。
1、新建一个注解类
package com.hmc.spring.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD}) //表示在元素上运行 @Retention(RetentionPolicy.RUNTIME) //注解在运行时有效 public @interface Log_meice { String value() default ""; }
2、加入元注解
@Target({ElementType.METHOD}) //表示在元素上运行 @Retention(RetentionPolicy.RUNTIME) //注解在运行时有效
3、测试
package com.hmc.spring.dao; import com.hmc.spring.annotation.Log_meice; public interface IUserDao { @Log_meice("增加了用户") void add(); @Log_meice("更新了用户") void update(); @Log_meice("删除了用户") void del(); void list(); }
四、设计模式
就是把所有的代码结构化分层,每一层职责明确。不论项目多么复杂,项目结构清晰明了。Model层
就是JavaBean,业务类的实体类;Dao层负责数据持久化,和数据库接入;Service层调用Dao层方法,实现
复杂的业务逻辑处理,Controller层主要做三件事:处理页面请求并接收参数、调用Service层方法、页面跳转。
各层层级关系明朗:Controller==>Service==>Dao 上层依赖于下层,下层为上层提供服务。
其实《金字塔结构》这本书讲解的是平时说话做事的逻辑层级;在代码世界里,本质也差不多。
五、三种方式实现AOP
1)经典方式:静态代理与动态代理
2)注解方式
3)XML配置方式
AOP——Aspect-Oriented Programming 面向切面编程
OC原则(open close);在不改编源码的情况下,为原代码织入新功能。
底层依赖的JDK的反射机制。反射我们不陌生了,之前JDBC一些列中,查的方法最后用到了Field.
1)经典方式:静态代理与动态代理
a、静态代理如何实现?
b、动态代理?
目的:UserService在调用Dao层add()方法时,为其增加日志信息。日志信息内容如下:
package com.hmc.spring.log; import java.util.Date; /** * User:Meice * 2017/11/5 */ public class Logger { public static void log(String info) { System.out.println(new Date()+"--->"+info); } }
a、静态代理如何实现?
package com.hmc.spring.dao; import com.hmc.spring.log.Logger; import org.springframework.stereotype.Repository; /** * User:Meice * 2017/11/5 */ @Repository public class UserProxyJdbcDao implements IUserDao { /** * 静态代理步骤: * 1、创建一个和要添加功能一模一样的Dao * 2、加入新增方法 * 3、注解(自动装配) */ public void add() { System.out.println("使用JDBC新增了用户...."); Logger.log("添加了用户"); } public void update() { } public void del() { } public void list() { } }
b、动态代理?
先单纯实现动态代理,不与spring整合。
package com.hmc.spring.proxy; import com.hmc.spring.dao.IUserDao; import com.hmc.spring.dao.UserJdbcDao; import com.hmc.spring.log.Logger; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * User:Meice * 2017/11/5 */ public class LoggerProxy implements InvocationHandler { //代理对象-->代言人 private Object target; public LoggerProxy(Object o) {//被代理对象->产品 this.target = o; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("嗯哈"); // Logger.log("Before:进行了相关操作"); Object obj = method.invoke(target,args); // Logger.log("After:进行了相关操作"); return obj; } public static void main(String[] args) { UserJdbcDao userJdbcDao = new UserJdbcDao();//被代理对象 LoggerProxy proxy = new LoggerProxy(userJdbcDao); //得到代理对象 /* IUserDao userDaoProxy =(IUserDao)Proxy.newProxyInstance(UserJdbcDao.class.getClassLoader(),IUserDao.class.getInterfaces(),proxy); userDaoProxy.add();*/ /** * 运行结果: * java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to com.hmc.spring.dao.IUserDao */ IUserDao ud =(IUserDao)Proxy.newProxyInstance(UserJdbcDao.class.getClassLoader(),UserJdbcDao.class.getInterfaces(),proxy); ud.add(); /** * 以上整体执行流程是这样的: * ud.add(),ud表示指定的接口(IUserDao)的代理实例,,该接口将add()方法的调用指派到指定的调用处理程序-proxy * 调用处理程序就是指实现InvocationHandler接口的这个类,也就是这里的LoggerProxy,只要把它实例化——通过带参构造 * 方法把被代理对象-UserJdbcDao传过去即可。 * * 最终,你看到的输出信息,就是这个类中invoke()方法体重输出的信息,而不是原有UserJdbcDao中的方法。 * 因为方法已经通过Method的invoke方法从底层映射过来了。 * * 再说简单点:也就是通过2步走就把UserJdbcDao中的add()方法复制了一份到这个调用处理程序中。然后在这里 * 加入我们需要增加的内容,而不改变原有方法。 * * 第一步:创建一个类,实现InvocationHandler接口,并实现方法。(这个类就是调用处理程序) * 第二步:通过Proxy这个类实例化该调用处理程序,实例化过程中就制定了代理产品-UserJdbcDao * * */ } }
通过spring.xml来实现动态代理
首先编写一个类,类似上面的。
package com.hmc.spring.proxy; import com.hmc.spring.annotation.Log_meice; import com.hmc.spring.dao.IUserDao; import com.hmc.spring.log.Logger; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * User:Meice * 2017/11/5 */ public class LoggerProxySpring implements InvocationHandler{ //制定代理人 private Object target; /* public LoggerProxySpring(Object o) { this.target = o; Proxy.newProxyInstance(o.getClass().getClassLoader(),o.getClass().getInterfaces(),this); }*/ /** * 构造方法无法返回Proxy实例化后被代理对象实现的接口 就会报错: No qualifying bean of type 'com.hmc.spring.dao.IUserDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired (required=true), @org.springframework.beans.factory.annotation. Qualifier(value=loggerProxySpring)} 表示希望返回IuserDao,实际却是loggerProxySpring这个实例 所以构造方法无法处理Proxy实例化这一步 */ //构造方法无法实例化Proxy所返回的被代理对象所实现的接口,所以要这么写 public static Object getInstance(Object o) { LoggerProxySpring proxy = new LoggerProxySpring(); proxy.target = o; return Proxy.newProxyInstance(o.getClass().getClassLoader(),o.getClass().getInterfaces(),proxy); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //对带有指定参数args的指定对象target调用由此 Method 对象表示的底层方法 //如果方法是list,不用打印日志信息 /* if (!"list".equals(method.getName()) || !"search".equals(method.getName())){ Logger.log("Before:进行了相关操作"); }*/ //注解 ——对加了注解的方法输出日志 Log_meice lo = method.getAnnotation(Log_meice.class); if(lo != null) { Logger.log("Before:进行了操作:"+lo.value()); } Object obj = method.invoke(target,args); Logger.log("After:进行了相关操作"); return obj; } }
配置spring.xml
<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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--扫描加入注解的包--> <context:component-scan base-package="com.hmc.spring"/> <!--配置动态代理--> <bean name="loggerProxySpring" class="com.hmc.spring.proxy.LoggerProxySpring" factory-method="getInstance" > <constructor-arg value="userJdbcDao"></constructor-arg> </bean> <!--配置BookDao的动态代理--> <bean name="bookProxySpring" class="com.hmc.spring.proxy.LoggerProxySpring" factory-method="getInstance"> <constructor-arg value="bookDao"></constructor-arg> </bean> </beans>
弊端:无法实现指定方法输出日志。
一般,查询方法不加日志,但是增修、修改、删除加日志,查询方法不加日志。如何处理?
做个判断:
if ("list".equals(method.getName())){ Logger.log("Before:进行了相关操作"); }
如果方法名不一致呢?加入多个判断?
//对带有指定参数args的指定对象target调用由此 Method 对象表示的底层方法
if (!"list".equals(method.getName()) || !"search".equals(method.getName())){ Logger.log("Before:进行了相关操作"); }
你能穷尽所有查的方法么?只要名字一变化,就不一样了…所以注解最方便:
2)注解方式实现AOP
@AspectJ-简化配置
有木有觉!得上面的动态代理很麻烦,比较复杂?其实笔者测试的时候,不甚顺利。注解方式实现AOP真的是So Easy!
(1)导入包aspectj包
只有导入这个包,才能用@AspectJ这个注解,不然没有呢
<!--导入AspectJWeaver包--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.12</version> </dependency>
(2)开启spring AOP支持
xmlns:aop="http://www.springframework.org/schema/aop" http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
要想开启支持,就需要有这个标签可以用,头部只有加入spring-aop,才会有下面的标签。
<!--开启spring AOP 支持--> <aop:aspectj-autoproxy/>
(3)定义切面
@Component @Aspect public class LoggerProxy { }
在代理类头部只加上@Aspect是不够的,还需要加上@Component
* @Aspect表明这是个切面类,里面会定义切入点。
@Component表明这个类在bean中所处的位置
* 因为它不属于Repository\Service\Controller
(4)定义通知
/** * 前置通知 *表达式匹配规则: * 第一个*:方法的修饰符(方法的返回类型) * 第二个*:service包下面的任意类 * 第三个*:service包下面的任意类的任意方法 * ..*表示service包下面的包的任意类 * */ @Before("execution(* com.hmc.spring.service.*.add*(..)) || " + "execution(* com.hmc.spring.service.*.del*(..)) ") public void BeforeMethod() { Logger.log("<<< 在调用方法前执行"); } /** * 后置通知 */ @After("execution(* com.hmc.spring.service.*.*(..))") public void AfterMethod() { Logger.log("在方法之后调用>>>"); //Logger.log("<<<环绕通知>>>"); } /** * 环绕方法 * 光加@Around是不行的,不会环绕 */ @Around("execution(* com.hmc.spring.service.*.*(..))") public void aroundMethod(ProceedingJoinPoint pjp) throws Throwable { Logger.log("<<<方法前后都会执行>>>"); pjp.proceed(); Logger.log("<<<方法前后都会执行>>>"); } /** * 测试异常执行通知 * AfterReturnning * 只有方法正常执行,才会调用 */ @AfterReturning("execution(* com.hmc.spring.service.*.*(..))") public void afterReturnning() { Logger.log("方法抛出异常之后才执行"); } /** * AfterThrowing表明 * 方法只有出现异常,才能看到这个日志 */ @AfterThrowing("execution(* com.hmc.spring.service.*.*(..))") public void afterThrowing() { Logger.log("方法抛出异常才能看到我"); } //连接点Joinpoint 可以更加精细化输出调用方法名等信息 @After("execution(* com.hmc.spring.service.*.*(..))") public void AfterMethod(JoinPoint jp) { System.out.println("正在执行的方法是:"+jp.getSignature().getName()); Logger.log("在方法之后调用>>>"); //Logger.log("<<<环绕通知>>>"); } /** * JoinPoint 可以输出执行方法的名称等详细信息 * org.aspectj.lang.JoinPoint; * * 运行结果: * 十一月 06, 2017 12:36:40 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [spring.xml] 正在执行的方法是:save Mon Nov 06 00:36:41 CST 2017 在方法之后调用>>> java.lang.NullPointerException * * * 有什么用?根据这个可以快捷找到哪个方法执行异常 * 以及其他更加精细化操作 */
3)XML配置方式实现AOP
注解有注解的好处,但是过多注解,也让人不知所云,而且注解最大弊端就是侵入性,直接改源代码去了,这时候配置就出场了。大型项目很多用配置,便于修改,结构清晰。
1、开启AOP支持
xmlns:aop="http://www.springframework.org/schema/aop" http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd[/code]
2、spring.xml中配置<!--为了可以使用注解,加入context头部信息并配置注解--> <context:component-scan base-package="com.hmc.spring"/> <!-- XML配置AOP--> <aop:config> <!--配置切面--> <aop:aspect ref="loggerProxy"> <!--配置切入点--> <aop:pointcut id="allMethod" expression="execution(* com.hmc.spring.service.*.*(..))" /> <!--<aop:before method="BeforeMethod" pointcut-ref="allMethod"/>--> <aop:after method="AfterMethod" pointcut-ref="allMethod"/> </aop:aspect> </aop:config>
测试:package com.hmc.spring; import com.hmc.spring.service.IUserService; import org.junit.jupiter.api.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * User:Meice * 2017/11/6 */ public class SpringTest { private static ApplicationContext ac; static { ac = new ClassPathXmlApplicationContext("spring.xml"); } @Test public void test() { IUserService ius = ac.getBean("userService", IUserService.class); ius.save(); } }
运行结果:十一月 06, 2017 8:44:15 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [spring.xml] 使用JDBC增加用户... Mon Nov 06 08:44:17 CST 2017 方法之后调用》》》
六、总结
1、构造方法注入,通过constructor-org来注入;
2、自动装配与注解。
在spring.xml头部加入context==》扫描加入注解的包==》主要有:@Component 通用的,还是有用的,比如Proxy那个类,就要加上。@Repository注解Dao层,
@Service注解Service层;@Controller层, @Autowired自动装配(byName byType);@Qualifier(value =”“)表明具体应用的类
非常方便,从此不用再配置。注解一旦加上,就代表已经实例化。
3、设计模式
就是把所有的代码结构化分层,每一层职责明确。不论项目多么复杂,项目结构清晰明了。Model层
就是JavaBean,业务类的实体类;Dao层负责数据持久化,和数据库接入;Service层调用Dao层方法,实现
复杂的业务逻辑处理,Controller层主要做三件事:处理页面请求并接收参数、调用Service层方法、页面跳转。
各层层级关系明朗:Controller==>Service==>Dao 上层依赖于下层,下层为上层提供服务。
4、自定义注解
新建一个@Annotation类==》设置默认值==》通过元注解定义注解具体作用域==》搞定!
5、AOP实现3中方式
1)代理模式
(1)静态代理-类似复制一个类,然后加入想要织入的功能;
(2)动态代理—本质还是复制了这个类,只是通过这几个步骤实现:新建一个类,实现接口InvocationHandler,重写方法;定义代理人和代理产品,通过构造方法赋值产品; ==>method.invoke实现方法的复制==》Proxy.newInstance实例话这个代理类,返回一个接口。
2)注解方式
spring.xml中加入头文件aop==》开启注解支持==》创建代理类,@Aspect定义切面;@Before等定义切入点及通知==》测试运行
3)XML方式
通过配置切面、切入点即可。就这么简单!
好了,午安!各位读者,下期再会!
相关文章推荐
- 整合框架 javaweb开发平台ssmy_m(与代码生成) java struts2 mybatis spring maven jquery
- java开源框架集成常见错误之spring2.5中没有org.springframework.web.struts.DelegatingActionProxy ,使得Action不能注入到spring中
- 2.1、Spring Web MVC是什么 Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职
- HzhJava框架搭建二:添加tomcat和spring运行一个简单web程序
- JavaWeb---框架SpringMvc+Mybatis开发项目第二季(共三季)
- JAVA_WEB项目之Struts2、Spring和Hibernate三大框架的整合
- Portal-Basic Java Web 应用开发框架:应用篇(十一) —— 整合 Spring
- java框架之spring(web环境配置)
- 【JavaWeb】Spring+SpringMVC+MyBatis+SpringSecurity+EhCache+JCaptcha 完整Web基础框架(前言)
- JavaWeb、J2-SE开发框架——Spring
- 【JavaWeb】Spring+SpringMVC+MyBatis+SpringSecurity+EhCache+JCaptcha 完整Web基础框架(一)
- Java语言介绍(04)开源项目(02)Web框架(01)Spring
- JAVA 中web 框架Spring 的核心 aop 和ioc
- JavaWeb---框架SpringMvc+Mybatis开发项目第三季(共三季)
- Java Web中选择Struts2,Spring等框架的分析
- java三大框架之spring手动配置web应用环境
- 2013-07-26 java web spring 框架获取资源文件
- JavaWeb开发基于Spring和mybatis框架(学习日志)
- Java Web开发框架Spring+Hibernate整合效果介绍(附源码)
- 集成框架 javaweb开发平台ssmy_m(生成代码) java struts2 mybatis spring maven jquery