整合 Spring 和 Servlet
DAO 使用 JDBC 通过模板类进行整合,模板类由 Spring 框架提供,只需进行配置即可
1、依赖:spring-jdbc 和连接池 druid、数据库驱动 mysql-connect-java
2、引入了 IoC、DI 后对象的创建完全交给 Spring 负责,只需要进行配置即可,所以在 Spring 的核心配置文件 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///test?serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!-- 配置 jdbc 模板类对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
3、定义 DAO 接口和对应的实现类,如果有必要还需要定义对应的实体类
public interface IUserDao {
public int insert(User user);
}
public class UserDaoImpl implements IUserDao{
private JdbcTemplate jdbcTemplate; //需要添加对应的 set 方法
public int insert(User user) {
return jdbcTemplate.update("insert into tb_users(username,password) values(?,?)",
user.getUsername(),user.getPassword()); }
}
4、配置DAO
<bean id="userDao" class="com.yan.dao.UserDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
5、测试
public class Test1 {
public static void main(String[] args) {
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
IUserDao userDao = ac.getBean("userDao", IUserDao.class);
User user=new User();
user.setUsername("zhangsan");
user.setPassword("123456");
int res=userDao.insert(user);
System.out.println(res); }
}
Spring 整合 Servlet 问题就是在 Servlet 中如何访问受管 bean?
1、添加依赖:spring-web 和 servlet-api,由于服务器中一般包含了 servet-api 相关的 jar 包,所以这里的依赖的 scope 应该设置为 provider,这个 servlet-api 依赖不会打包到应用中
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.27</version>
</dependency>
<dependency>
<gruopId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
2、在 src/main 目录下创建 webapp/WEB-INF 文件夹,添加 web 应用的核心配置文件
web.xml在应用启动时需要通过 xxx 加载 spring 的配置文件,并创建对应的 IoC/DI 容器,再将容器存储到 application对象中,供所有用户共享使用
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0">
<!-- 配置 Spring 核心配置文件的名称和位置 -->
<context-param> 上下文参数,供 ContextLoaderListener 使用
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 当应用启动时,也就是 application 对象创建时,自动创建 IoC 容器并存储到 application 对象中,供
所有用户共享访问 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
在 web 应用中使用 WebApplicationContext 引用 IoC 容器,WebApplicationContext 接口是 ApplicationContext接口的子接口,主要添加了针对 web 应用的相关方法
3、定义控制器测试
public class MyController extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取 applicationContext 对象
ApplicationContext ac=
WebApplicationContextUtils.getRequiredWebApplicationContext(this.getServletContext());//通过工具类获取
//IoC 容器的引用,参数为 application 对象,获取 application 对象的方法由 HttpServlet 父类提供
Date now=ac.getBean("now", Date.class);
System.out.println(now);
}
}
针对 servlet 配置
<servlet>
<servlet-name>test</servlet-name>
<servlet-class>com.yan.action.MyController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>test</servlet-name>
<url-pattern>/test.do</url-pattern>
</servlet-mapping>
web 应用组件
1、Servlet 通过映射的 url 地址的访问触发执行,或者页面跳转触发执行。3 生命周期方法 init-service-destroy
2、Filter 通过映射的 url 地址的访问触发执行,就是当访问某个地址时,会在执行目标程序之前或者之后进行一些额外的处理,当然也可以生成响应替代目标访问结果。实际上就是一种 AOP 编程
3、Listener 通过特定事件触发执行
监听 request 对象:
监听创建和销毁 ServletRequestListener
监听针对 request 的 attribute 的增删改操作 ServletRequestAttributeListener
监听 session 对象
创建和销毁 HttpSessionListener
针对 session 的 attribute 的增删改操作 HttpSessionAttributeListener
针对 session 的激活钝化操作 HttpSessionActivationListener
针对 bean 的绑定操作 HttpSessionBindingListener
监听 application 对象
创建和销毁 ServletContextListener
针对 application 的 attribute 的增删改操作 ServletContextAttributeListener
AOP
在应用中使用日志工具,例如 log4j
1、添加依赖 log4j
2、通过工具类获取对应的日志记录器对象
private static final Logger logger= LoggerFactory.getLogger(UserServImpl.class);
3、调用日志记录器中的方法实现日志输出 logger.debug(“需要输出到日志中的信息”);
如果把通用系统需求加入到处理业务需求的类中,那么业务类就会变得很复杂和零乱。甚至可能会喧宾夺主,而且以后要变更系统需求的时候,将给软件带来很大的维护量
分散关注的思路
1、将通用需求功能从不相关类之中分离出来
2、能够使得很多类共享一个行为,一旦行为发生变化,不必修改很多类,只要修改这个行为就可以
3、AOP 就是这种实现分散关注的编程方法,它将关注【通用功能】封装在方面【切面】中
代理模式
意图:为其他对象提供一种代理以控制对这个对象的访问。
优点: 1、职责清晰。 2、高扩展性。 3、智能化。
缺点:
1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
注意事项: 1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。 2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。
静态代理
需要聘请一个歌手来唱歌助兴,实际上没有办法直接访问歌手,而是通过代理人访问歌手
定义接口
public interface 会唱歌的{
public void 唱歌();
}
被代理对象所属的类
//注意这里不要讨论类与对象的问题,仅仅是了方便理解
public class 刘德华 implements 会唱歌的{
@Override
public void 唱歌(){
System.out.println("冷冷的冰雨~~~");
}
}
代理对象所属的类
public class 宋喆 implements 会唱歌的{
private 会唱歌的 被代理对象;
public 宋喆(会唱歌的 target){
this.被代理对象=target;
}
Override
public void 唱歌(){
System.out.println("签订演艺合同...");
//由代理人限制对被代理对象的访问
this.被代理对象.唱歌();
System.out.println("收钱");
}
}
测试程序
会唱歌的 obj = new 宋喆(new 刘德华());
obj.唱歌();
缺点:需要手动创建子类,当需要被代理的元素较多时工作量会明显变大
动态代理
动态代理有 JDK 动态代理和 CGLib 动态代理两种
定义接口
public interface 会唱歌的{
public void 唱歌();
}
被代理对象所属的类
//注意这里不要讨论类与对象的问题,仅仅是为了方便
public class 刘德华 implements 会唱歌的(){
@Override
public void 唱歌(){
System.out.println("冷冷的冰雨~~~");
}
}
使用 JDK 的动态代理时没有对应的代理类,这个代理对象是动态生成的,这里需要定义一个回调处理类
public class 回调处理类 implements InvocationHandler{
private Object 被代理对象;
public 回调处理类(Object obj){
this.被代理对象=obj;
}
Override
public Object invoke(Object proxy,Method method, Object[] args) throw Throwable{
System.out.println("签订演艺合同...");
Object res = method.invoke(this.被代理对象,args);//通过反射机制调用被代理对象中的对应方法
System.out.println("收钱");
return res;
}
}
测试调用
//创建一个代理对象,参数1为所用的类加载器对象,参数2为代理的接口数组,参数为回调接口实现对象
会唱歌的 obj = (会唱歌的)Proxy.newPrixyInstance(会唱歌的.class.getClassLoader(),
new Class[]{会唱歌的.class},new 回调处理类(new 刘德华()));
obj.唱歌();
AOP 实现的原理就是 JDK 动态代理模型。JDK 动态代理只能为实现接口的 Java 类创建代理对象;CGLib 可以为任何 Java 类提供代理对象。
总结
1、静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;核心:手动创建一个与目标类相同接口的子类,包装目标类。
2、JDK 动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;通过 jdk 提供的反射包中 Proxy 类动态的创建一个与目标类实现相同接口的子类对象,包装目标。
3、CGlib 动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理。通过CGlib 提供的 Enhancer 这个类,动态的创建一个目标类的子类对象,包装目标类。在逐步对 JDK 动态代理优化后,在调用次数较少的情况下,JDK 代理效率高于 CGLIB 代理效率,只有当进行大量调用的时候,jdk6 和 jdk7 比 CGLIB 代理效率低一点,但是到 jdk8 的时候,jdk 代理效率高于 CGLIB 代理。
Hello Spring AOP
1、依赖 spring-aop
Spring AOP 默认采用的是 JDK 动态代理,所以要求接口;但是实际上没有接口 Spring 会切换到 CGLib 动态代理。
2、定义接口
public interface 会唱歌的{
public void 唱歌();
}
3、被代理对象所属的类
//注意这里不要讨论类与对象的问题,仅仅是了方便理解
public class 刘德华 implements 会唱歌的{
@Override
public void 唱歌(){
System.out.println("冷冷的冰雨~~~");
}
}
4、代理对象所属的类
public class 前置处理类 implements MethodBeforeAdvice{
@Override
public void before (Method method,Object[] args,Object tsrget)throw Throwable{
//参数1是调用的方法对象,参数2是调用的方法参数,参数3是目标对象
System.out.println(target);
System.out.println("签订演艺合同...");
}
}
5、添加对应的配置
<bean id="被代理对象" class="com.test2.刘德华"/>
<bean id="回调处理对象" class="com.test2.前置处理类"/>
<bean id="代理对象" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="被代理对象"/>
<property name="interfaces"><!--配置被代理的接口 -->
<value>com.test2.会唱歌的</value>
</property>
<property name="interceptorNames"><!--回调处理的对象名称 -->
<value>回调处理对象</value>
</property>
</bean>
6、测试调用
ApplicationContext ac = new
ClassPathXmlApplicationContext("applicationContext.xml");
会唱歌的 s = ac.getBean("代理对象",会唱歌的.class);
s.唱歌();
概述 AOP
AOP 面向切面编程可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序增加动态控制功能的一种技术。AOP 的主要编程对象是切面,而切面模块化横切关注点
在应用 AOP 编程时仍然需要定义公共功能,但可以明确的定义这个功能在哪里,以什么方式应用,并且不必修改受影响的类。这样一来横切关注点就被模块化到特殊的对象即切面里
面向切面编程AOP通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是 Spring 框架中的一个重要内容,是函数式编程的一种衍生范型。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
基础概念
连接点:添加程序的位置,Spring 支持的颗粒度比较粗,只能是方法前、方法后等位置,不能是语句级
切入点:连接点的对象化表示,可以使用 AspectJ 的切入点表达式说面一系列连接点
通知:需要添加的程序
切面:切入点+通知
增强和织入
AOP 与 IoC 的联系与区别
相同点:都是寻求调用者与被调用者的解耦
不同点:
1、IoC 关注的是对象的创建、维护职责与对象的使用权(该使用权与调用者的功能紧密相关)解耦
2、AOP 关注的是功能本身的解耦
Spring 中 AOP 代理由 Spring 的 IoC 容器负责生成、管理,其依赖关系也由 IoC 容器负责管理。因此,AOP 代理可以直接使用容器中的其它 bean 实例作为目标,这种关系可由 IOC 容器的依赖注入提供。
Spring 创建代理的规则为:
1、默认使用 Java 动态代理来创建 AOP 代理,这样就可以为任何接口实例创建代理了
2、当需要代理的类不是代理接口的时候,Spring 会切换为使用 CGLIB 代理,也可强制使用 CGLIB。配置方法
<aop:aspectj-autoproxy proxyt-target-class="true"/>