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