目录
一、Spring 框架简介及官方压缩包目录介绍
二、IOC
三、Spring环境的搭建
四、Spring的三种创建对象方法
五、如何给bean的属性赋值(注入)
六、DI(依赖注入)
七、Spring整合Mybatis
八、AOP()
一、Schema-based 实现步骤
二、配置异常通知的步骤(AspectJ)
三、异常通知(schema-based)
四、环绕通知(Schema-based 方式)
五、AspectJ的实现
六、使用注解配置AOP
七、代理设计设置
八. 静态代理设计模式
九、动态代理
九、其他
①、自动注入
②Spring 中加载 properties 文件(软编码)
③scope属性
④单例设计模式
⑤声明式事务
⑥声明式事务中的属性解释
⑦Spring中常用注解
⑧Ajax
能力提升:
一、利用验证码登录
一、Spring 框架简介及官方压缩包目录介绍
- 主要发明者:RodJohnson
- 轮子理论推崇者:
- 轮子理论:不用重复发明轮子
- IT行业:直接使用写好的代码
- Spring 框架宗旨:不重新发明技术,让原有技术使用起来更加方便.
- Spring几大核心功能
- IoC/DI 控制反转/依赖注入
- AOP 面向切面编程
- 声明式事务
- Spring 框架 runtime
- test:spring:提供测试功能
- CoreContainer:核心容器.Spring 启动最基本的条件
- Beans:Spring 负责创建类对象并管理对象
- Core: 核心类
- Context:”上下文参数.获取外部资源或这管理注解等
- SpEl:expression.jar
- AOP: 实现 aop 功能需要依赖
- Aspects: 切面 AOP 依赖的包
- DataAccess/Integration:spring 封装数据访问层相关内容
- JDBC:Spring 对 JDBC 封装后的代码
- ORM: 封装了持久层框架的代码.例如 Hibernate
- transactions:对应 spring-tx.jar,声明式事务使用.
- WEB:需要 spring 完成 web 相关功能时需要
- 例如:由tomcat加载spring配置文件时需要有spring-web包
- Spring 框架中重要概念
- 容器(Container):Spring 当作一个大容器.
- BeanFactory 接口.老版本
- 新版本中 ApplicationContext 接口,是 BeanFactory 子接口.BeanFactory 的功能在 ApplicationContext 中都有
- 从 Spring3 开始把 Spring 框架的功能拆分成多个 jar.
- Spring2 及以前就一个 jar
二、IOC
- 中文名称:控制反转
- 英文名称:(Inversion of Control)
- IoC 是什么?
- IoC 完成的事情原先由程序员主动通过 new 实例化对象事情,转交给 Spring 负责
- 控制反转中控制指的是:控制类的对象.
- 控制反转中反转指的是转交给 Spring 负责.
- IoC 最大的作用:解耦
- 程序员不需要管理对象.解除了对象管理和程序员之间的耦合
三、Spring环境的搭建
1、导入jar包
四个核心包和一个日志包
2、在src下新建applicationContext.xml
- 文件名称和路径自定义.
- 记住 Spring 容器 ApplicationContext,applicationContext.xml 配置的信息最终存储到了 AppliationContext 容器中
- spring 配置文件是基于 schema
- 比 DTD 具备更好的扩展性.
- 每次引入一个 xsd 文件是一个 namespace(xmlns)
- 配置文件中只需要引入基本 schema
- 通过<bean/> 创建对象.
- 默认配置文件被加载时创建对象
<?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="peo" class="com.tk.pojo.People"></bean>
</beans>
3、编写测试方法
- getBean(“<bean>标签id值”,返回值类型);如果没有第二个参数,默认是 Object
- getBeanDefinitionNames(),Spring 容器中目前所有管理的所有对象.
package com.tk.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.tk.pojo.People;
public class Test {
public static void main(String args[]) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
People peo = ac.getBean("peo",People.class);
System.out.println(peo);
String[] strpeos = ac.getBeanDefinitionNames();
for (String string : strpeos) {
System.out.println(string);
}
}
}
四、Spring的三种创建对象方法
1、通过构造方法创建
- 无参构造创建:默认情况.
- 有参构造创建:需要明确配置
- 需要在类中提供有参构造方法
- 在 applicationContext.xml 中设置调用哪个构造方法创建对象
- 如果设定的条件匹配多个构造方法执行最后的构造方法
- index: 参数的索引,从 0 开始
- name: 参数名
- type:类型(区分开关键字和封装类 int 和 Integer)
<?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="peo" class="com.tk.pojo.People">
<constructor-arg index="0" name="id" type="int" value="1"></constructor-arg>
<constructor-arg index="1" name="name" type="String" value="张三"></constructor-arg>
</bean>
</beans>
2、实例工厂
- 工厂设计模式:帮助创建类对象.一个工厂可以生产多个对象
- 实例工厂:需要先创建工厂,才能生产对象
- 实现步骤:
- 必须要有一个实例工厂
package com.tk.pojo;
public class PeopleFactory {
public People getInstance() {
return new People();
}
}
- 在 applicationContext.xml 中配置工厂对象和需要创建的对象
<bean id="peopleFactory" class="com.tk.pojo.PeopleFactory"></bean>
<bean id="peo1" factory-bean="peopleFactory" factory-method="getInstance"></bean>
3、静态工厂
- 不需要创建工厂,快速创建对象
- 实现步骤
- 编写一个静态工厂(在方法上添加 static
package com.tk.pojo;
public class PeopleFactory {
public static People getInstance() {
return new People();
}
}
<bean id="peo3" class="com.tk.pojo.PeopleFactory" factory-method="getInstance"></bean>
五、如何给bean的属性赋值(注入)
- 通过构造方法
- 设置注入(通过set方法)
- 如果属性是基本数据类型或 String 等简单
<bean id="peo" class="com.tk.pojo.People">
<property name="id" value="1"></property>
<property name="name" value="宋旭东"></property>
</bean>
- 等价于
<bean id="peo" class="com.tk.pojo.People">
<property name="id">
<value>1</value>
</property>
<property name="name">
<value>宋旭东</value>
</property>
</bean>
- 如果属性是 Set<?>
<property name="set">
<set>
<value>1</value>
<value>2</value>
<value>3</value>
<value>4</value>
</set>
</property>
- 如果属性是List
<property name="list">
<list>
<value>1</value>
<value>2</value>
<value>3</value>
<value>4</value>
</list>
</property>
- 如果List中只有一个值
<property name="list" value="1"> </property>
- 如果属性是数组
<property name="a">
<array>
<value>1</value>
<value>2</value>
<value>3</value>
</array>
</property>
- 如果属性是map
<property name="map">
<map>
<entry key="a" value="b"></entry>
<entry key="c" value="d"></entry>
</map>
</property>
- 如果属性是Properties类型
<property name="pros">
<props>
<prop key="a">b</prop>
<prop key="c">d</prop>
</props>
</property>
六、DI(依赖注入)
- DI:中文名称:依赖注入
- 英文名称((Dependency Injection)
- DI 是什么?
- DI 和 IoC 是一样的
- 当一个类(A)中需要依赖另一个类(B)对象时,把 B 赋值给 A 的过程就叫做依赖注入.
<bean id="peo" class="com.bjsxt.pojo.People">
<property name="desk" ref="desk"></property>
</bean>
<bean id="desk" class="com.bjsxt.pojo.Desk">
<property name="id" value="1"></property>
<property name="price" value="12"></property>
</bean>
七、Spring整合Mybatis
1、导 入 mybatis 所 有 jar 和 spring 基 本包,spring-jdbc,spring-tx,spring-aop,spring-web,spring 整合 mybatis 的包等
2、先配置web.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name></display-name>
<!-- spring配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 添加一个监听器,帮助加载spring的配置文件 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
3、编写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="datasourse" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="jdbc:mysql://localhost:3306/rejava?useUnicode=true"></property>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="username" value="root"></property>
<property name="password" value="1327148745"></property>
</bean>
<!-- 产生SqlSessionFactory对象 -->
<bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 数据库连接信息来源于dataSource -->
<property name="dataSource" ref="datasourse"></property>
</bean>
<!-- 扫描器相当于mybatis.xml里面的mappers下的package标签 扫描mapper包后会给相对应接口创建对象-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer" >
<!-- 要扫描的包 -->
<property name="basePackage" value="com.tk.mapper"></property>
<!-- 和factory产生关系 -->
<property name="sqlSessionFactory" ref="factory"></property>
</bean>
<!-- 由spring来管理service实现类 -->
<bean id="student" class="com.tk.service.impl.StudentServiceImpl">
<property name="sm" ref="studentMapper"></property>
</bean>
</beans>
4、编写代码
- 正常编写 pojo
- 编写 mapper 包下时必须使用接口绑定方案或注解方案(必须有接口)
- 正常编写 Service 接口和 Service 实现类
- 需要在 Service 实现类中声明 Mapper 接口对象,并生成get/set 方法
- spring 无法管理 Servlet,在 service 中取出 Servie 对象
public class StudentServiceImpl implements StudentService{
private StudentMapper sm;
public StudentMapper getSm() {
return sm;
}
public void setSm(StudentMapper sm) {
this.sm = sm;
}
@Override
public List<Student> selAllInfo() {
return sm.selAll();
}
}
public class Show extends HttpServlet {
private StudentService ss = null;
@Override
public void init() throws ServletException {
ApplicationContext ac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
ss = ac.getBean("student",StudentServiceImpl.class);
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
resp.setCharacterEncoding("utf-8");
req.setCharacterEncoding("utf-8");
req.setAttribute("info", ss.selAllInfo());
System.out.println(ss.selAllInfo());
req.getRequestDispatcher("index.jsp").forward(req, resp);
}
}
八、AOP()
- .AOP:中文名称:面向切面编程
- 英文名称:(Aspect Oriented Programming)
- 正常程序执行流程都是纵向执行流程
- 又叫面向切面编程,在原有纵向执行流程中添加横切面
- 不需要修改原有程序代码
- 高扩展性
- 原有功能相当于释放了部分逻辑,让职责更加明确
- 面向切面编程是什么?
- 在程序原有纵向执行流程中,针对某一个或某一些方法添加通知,形成横切面过程就叫做面向切面编程.
- 常用概念
- 原有功能: 切点,pointcut
- 前置通知: 在切点之前执行的功能.beforeadvice
- 后置通知: 在切点之后执行的功能,afteradvice
- 如果切点执行过程中出现异常,会触发异常通知.throwsadvice
- 所有功能总称叫做切面.
- 织入: 把切面嵌入到原有功能的过程叫做织入
- spring 提供了 2 种 AOP 实现方式
- Schema-based
- 每个通知都需要实现接口或类
- 配置 spring 配置文件时在<aop:config>配置
- AspectJ
- 每个通知不需要实现接口或类
- 配置 spring 配置文件是在<aop:config>的子标签<aop:aspect>中配置
一、Schema-based 实现步骤
1、导入jar
2、新建通知类
- 新建前置通知
- arg0: 切点方法对象 Method 对象
- arg1: 切点方法参数
- arg2:切点在哪个对象中
public class MyBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method arg0, Object[] arg1, Object arg2)
throws Throwable {
System.out.println("我是前置通知");
}
}
- 新建后置通知
- arg0: 切点方法返回值
- arg1:切点方法对象
- arg2:切点方法参数
- arg3:切点方法所在类的对象
public class MyAfterAdvice implements AfterReturningAdvice{
@Override
public void afterReturning(Object arg0, Method arg1, Object[] arg2,
Object arg3) throws Throwable {
System.out.println("我是后置通知");
}
}
- 配置 spring 配置文件
- 引入 aop 命名空间.
- 配置通知类的<bean>
- 配置切面
- * 通配符,匹配任意方法名,任意类名,任意一级包名
- eg: com.*.service.*(..) com包下的任意包的service包的任意方法
- 如果希望匹配任意方法参数 (..)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 配置通知类对象,在切面引入 -->
<bean id="mybefore" class="com.tk.advice.MyBeforeAdvice"></bean>
<bean id="myafter" class="com.tk.advice.MyAfterAdvice"></bean>
<!-- 配置界面 -->
<aop:config>
<!-- 配置切点
如果切点有参数数
<aop:pointcut expression="execution(* com.tk.test.Demo.demo2(String,int) and args(name,age))" id="mypoint"/>
-->
<aop:pointcut expression="execution(* com.tk.test.Demo.demo2())" id="mypoint"/>
<!-- 通知 -->
<aop:advisor advice-ref="mybefore" pointcut-ref="mypoint"/>
<aop:advisor advice-ref="myafter" pointcut-ref="mypoint"/>
</aop:config>
<!-- 用于测试 -->
<bean id="demo" class="com.tk.test.Demo"></bean>
</beans>
二、配置异常通知的步骤(AspectJ)
- 只有当切点报异常才能触发异常通知
- 在 spring 中有 AspectJ 方式提供了异常通知的办法.
- 如果希望通过 schema-base 实现需要按照特定的要求自己编写方法
- 实现步骤:
- 新建类,在类写任意名称的方法
package com.tk.advice;
public class MyThrowAdvice {
private void myException(Exception e){
System.out.println("程序出现问题"+e.getMessage());
}
}
- 在 spring 配置文件中配置
- <aop:aspect>的 ref 属性表示:方法在哪个类中
- <aop:xxxx/> 表示什么通知
- method: 当触发这个通知时,调用哪个方法
- throwing: 异常对象名,必须和通知中方法参数名相同(可以不在通知中声明异常对象)
<bean id="myException" class="com.tk.advice.MyThrowAdvice"></bean>
<aop:config>
<aop:aspect ref="myException">
<aop:pointcut expression="execution(* com.tk.test.Demo.demo2())" id="mypoint"/>
<aop:after-throwing method="myException" pointcut-ref="mypoint" throwing="e"/>
</aop:aspect>
</aop:config>
<!-- 用于测试 -->
<bean id="demo" class="com.tk.test.Demo"></bean>
三、异常通知(schema-based)
- 新建一个类实现 throwsAdvice 接口
- 必须自己写方法,且必须叫 afterThrowing
- 有两种参数方式
- 必须是 1 个或 4 个
- 异常类型要与切点报的异常类型一致
package com.tk.advice;
import java.lang.reflect.Method;
import org.springframework.aop.ThrowsAdvice;
public class MyThrowAdvice implements ThrowsAdvice{
public void afterThrowing(Method me,Object[] args,Object target,Exception ex){
System.out.println("四个参数的错误类"+ex.getMessage());
}
public void afterThrowing(Exception ex){
System.out.println("一个参数的错误类"+ex.getMessage());
}
}
<bean id="myException" class="com.tk.advice.MyThrowAdvice"></bean>
<aop:config>
<aop:pointcut expression="execution(* com.tk.test.Demo.demo2())" id="mypoint"/>
<aop:advisor advice-ref="myException" pointcut-ref="mypoint"/>
</aop:config>
<!-- 用于测试 -->
<bean id="demo" class="com.tk.test.Demo"></bean>
四、环绕通知(Schema-based 方式)
1、把前置通知和后置通知都写到一个通知中,组成了环绕通知
实现:新建一个类实现 MethodInterceptor(方法拦截器)
package com.tk.advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class MyArround implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation arg0) throws Throwable {
System.out.println("环绕前置");
Object result = arg0.proceed(); //放行,调用切点方法
System.out.println("环绕后置");
return result;
}
}
<bean id="myArround" class="com.tk.advice.MyArround"></bean>
<aop:config>
<aop:pointcut expression="execution(* com.tk.test.Demo.demo2())" id="mypoint"/>
<aop:advisor advice-ref="myArround" pointcut-ref="mypoint"/>
</aop:config>
<!-- 用于测试 -->
<bean id="demo" class="com.tk.test.Demo"></bean>
五、AspectJ的实现
1、新建类,不用实现,并且 类中方法名任意
package com.tk.advice;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAdvice {
public void myBeforeArgs(String name,int age){
System.out.println("I'm before"+name+" "+age);
}
public void myBefore(){
System.out.println("I'm before");
}
public void myAfter(){
System.out.println("I'm After");
}
public void myAfterturning(){
System.out.println("I'm AfterTurning");
}
public void myThrowing(){
System.out.println("I'm Throwing");
}
public Object myArround(ProceedingJoinPoint p) throws Throwable{
System.out.println("I'm Arround Before");
Object obj = p.proceed();
System.out.println("I'm Arround After");
return obj;
}
}
2、配置spring配置文件
- <aop:after/> 后置通知,是否出现异常都执行
- <aop:after-returing/> 后置通知,只有当切点正确执行时
- <aop:after/> 和 <aop:after-returing/> 和<aop:after-throwing/>执行顺序和配置顺序有关
- execution() 括号不能扩上 args
- 中间使用 and 不能使用&& 由 spring 把 and 解析成&&
- args(名称) 名称自定义的.顺序和 demo2(参数,参数)对应
- arg-names=” 名 称 ” 名 称 来 源 于expression=” ” 中 args(),名称必须一样
- args() 有几个参数,arg-names 里面必须有几个参数
- arg-names=”” 里面名称必须和通知方法参数名对应
<bean id="myAdvice" class="com.tk.advice.MyAdvice"></bean>
<aop:config>
<aop:aspect ref="myAdvice">
<aop:pointcut expression="execution(* com.tk.test.Demo.demo2())" id="mypoint"/>
<aop:pointcut expression="execution(* com.tk.test.Demo.demo2(String,int)) and args(name,age)" id="mypoint1"/>
<aop:before method="myBeforeArgs" pointcut-ref="mypoint1" arg-names="name,age"/>
<aop:before method="myBefore" pointcut-ref="mypoint"/>
<aop:after method="myAfter" pointcut-ref="mypoint"/>
<aop:after-returning method="myAfterturning" pointcut-ref="mypoint"/>
<aop:around method="myArround" pointcut-ref="mypoint"/>
<aop:after-throwing method="myThrowing" pointcut-ref="mypoint"/>
</aop:aspect>
</aop:config>
<bean id="demo" class="com.tk.test.Demo"></bean>
六、使用注解配置AOP
- spring 不会自动去寻找注解,必须告诉 spring 哪些包下的类中可能
- 导入xmlns:context
<context:component-scan base-package="com.tk.test,com.tk.advice"></context:component-scan>
<!-- proxy-target-class
true 使用cglib动态代理
false 使用jdl动态代理
-->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
- @Component
- 相当于<bean/>
- 如果没有参数,把类名首字母变小写,相当于<beanid=””/>
- @Component(“自定义名称”)
- 实现步骤
- 在 spring 配置文件中设置注解在哪些包中
<context:component-scan base-package="com.tk.test,com.tk.advice"></context:component-scan>
- 在 Demo 类中添加@Componet
- 在方法上添加@Pointcut(“ ”) 定义切点
package com.tk.test;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
public class Demo {
@Pointcut("execution(* com.tk.test.Demo.demo2())")
public void demo2(){
System.out.println("demo2");
}
}
- 在通知类中配置
- @Component 类被 spring 管理
- @Aspect 相当于<aop:aspect/>表示通知方法在当前类中
package com.tk.advice;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class MyAdvice {
@Before("execution(* com.tk.test.Demo.demo2())")
public void myBefore(){
System.out.println("I'm before");
}
@After("execution(* com.tk.test.Demo.demo2())")
public void myAfter(){
System.out.println("I'm After");
}
@AfterReturning("execution(* com.tk.test.Demo.demo2())")
public void myAfterturning(){
System.out.println("I'm AfterTurning");
}
@AfterThrowing("execution(* com.tk.test.Demo.demo2())")
public void myThrowing(){
System.out.println("I'm Throwing");
}
@Around("execution(* com.tk.test.Demo.demo2())")
public Object myArround(ProceedingJoinPoint p) throws Throwable{
System.out.println("I'm Arround Before");
Object obj = p.proceed();
System.out.println("I'm Arround After");
return obj;
}
}
七、代理设计设置
- 设计模式:前人总结的一套解决特定问题的代码.
- 代理设计模式优点:
- 保护真实对象
- 让真实对象职责更明确.
- 扩展
- 代理设计模式
- 真实对象.(老总)
- 代理对象(秘书)
- 抽象对象<接口>(抽象功能),谈小目标
八. 静态代理设计模式
- 由代理对象代理所有真实对象的功能.
- 自己编写代理类
- 每个代理的功能需要单独编写
- 静态代理设计模式的缺点:
- 当代理功能比较多时,代理类中方法需要写很多
真实对象
package com.tk.proxy;
public class Laozong implements Gongneng {
private String name;
public Laozong(String name) {
super();
this.name = name;
}
public void tianXiaoMuBiao(){
System.out.println("谈小目标");
}
public void eat(){
System.out.println("吃饭");
}
}
代理对象
package com.tk.proxy;
public class Mishu implements Gongneng{
private Laozong laozong = new Laozong("云云");
@Override
public void eat() {
System.out.println("预约时间");
laozong.eat();
System.out.println("备注");
}
@Override
public void tianXiaoMuBiao() {
System.out.println("预约时间");
laozong.tianXiaoMuBiao();
System.out.println("备注");
}
}
抽象方法
package com.tk.proxy;
public interface Gongneng {
void eat();
void tianXiaoMuBiao();
}
检测
public static void main(String args[]) {
Gongneng gn = new Mishu();
gn.eat();
gn.tianXiaoMuBiao();
}
九、动态代理
- 为了解决静态代理频繁编写代理功能缺点
- 分类
- JDK 提供的
- cglib 动态代理
1、JDK
- 和 cglib 动态代理对比
- 优点:jdk 自带,不需要额外导入 jar
- 缺点:
- 真实对象必须实现接口
- 利用反射机制.效率不高.
真实对象 和 抽象方法(功能)不变
代理对象
package com.tk.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class Mishu implements InvocationHandler{
private Laozong laozong = new Laozong("云云");
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("预约时间");
Object invoke = method.invoke(laozong, args);
System.out.println("备注");
return invoke;
}
}
测试方法
public static void main(String args[]) {
System.out.println(Me.class.getClassLoader() == Laozong.class.getClassLoader());
/*
* 参数一:反射时用的类加载器
* 参数二:Proxy需要实现什么借口
* 参数三:通过接口对象调用方法时,需要调用那个类的invoke
*/
Gongneng newProxyInstance = (Gongneng) Proxy.newProxyInstance(Me.class.getClassLoader(), new Class[]{Gongneng.class}, new Mishu());
newProxyInstance.eat();
newProxyInstance.tianXiaoMuBiao();
}
2、cjlib
- cglib 优点:
- 基于字节码,生成真实对象的子类.
- 运行效率高于 JDK 动态代理.
- 不需要实现接口
- cglib 缺点:
- 非 JDK 功能,需要额外导入 jar
- 使用 springaop 时,只要出现 Proxy 和真实对象转换异常
- 设置为 true 使用 cglib
- 设置为 false 使用 jdk(默认值)
<!-- proxy-target-class
true 使用cglib动态代理
false 使用jdl动态代理
-->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
真实对象 和 抽象方法(功能)不变
代理对象
package com.tk.proxy;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class Mishu implements MethodInterceptor{
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2,
MethodProxy arg3) throws Throwable {
System.out.println("预约吃饭");
//invoke()调用子类重写的方法
Object invokeSuper = arg3.invokeSuper(arg0, arg2);
System.out.println("记录");
return invokeSuper;
}
}
测试方法
package com.tk.proxy;
import org.springframework.cglib.proxy.Enhancer;
public class Me {
public static void main(String args[]) {
Enhancer en = new Enhancer();
en.setSuperclass(Laozong.class);
en.setCallback(new Mishu());
Laozong laozong = (Laozong) en.create();
laozong.eat();
laozong.tianXiaoMuBiao();
}
}
九、其他
①、自动注入
自动注入的优先级特别高
- 在 Spring 配置文件中对象名和 ref=”id” id 名相同使用自动注入,可以不配置<property/>
- 不配置<property/>
- 在<be
- an>中通过 autowire=”” 配置,只对这个<bean>生效
- 在<beans>中通过 default-autowire=””配置,表当当前文件中所
有<bean>都是全局配置内容
- autowire=”” 可取值
- default: 默认值,根据全局 default-autowire=””值.默认全局和局部都没有配置情况下,相当于 no
- no: 不自动注入
- byName: 通过名称自动注入.在 Spring 容器中找类的 Id
- byType: 根据类型注入.
- spring 容器中不可以出现两个相同类型的<bean>
- constructor: 根据构造方法注入.
- 提供对应参数的构造方法(构造方法参数中包含注入对戏那个)
- 底层使用 byName, 构造方法参数名和其他<bean>的 id相同.
②Spring 中加载 properties 文件(软编码)
- 在 src 下新建 xxx.properties 文件
- 在 spring 配置文件中先引入 xmlns:context,在下面添加
- 如果需要记载多个配置文件逗号分割
<context:property-placeholder location="classpath:jdbc.properties"/>
- 添加了属性文件记载,并且在<beans>中开启自动注入注意的地方
- SqlSessionFactoryBean 的 id 不能叫做 sqlSessionFactory
- 修改
- 把原来通过ref引用替换成value赋值,自动注入只能影响ref,不会影响 value 赋值
<!-- mapper -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.tk.mapper"></property>
<property name="sqlSessionFactoryBeanName" value="factory"></property>
</bean>
- 使用属性中的数据
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
- 在被Spring管理的类中通过@Value(“${key}”)取出properties中内容
- 添加注解扫描(不扫描无法差别出来)
<context:component-scan base-package="com.tk.service.impl"></context:component-scan>
- 在类中添加
- key 和变量名可以不相同
- 变量类型任意,只要保证 key 对应的 value 能转换成这个
类型就可以.
public class UserServiceImpl implements UserService{
@Value("${myname}")
private String name;
③scope属性
- <bean>的属性,
- 作用:控制对象有效范围(单例,多例等)
- <bean/>标签对应的对象默认是单例的.
- 无论获取多少次,都是同一个对象
- scope 可取值
- singleton 默认值,单例
- prototype 多例,每次获取重新实例化
- request 每次请求重新实例化
- session 每个会话对象内,对象是单例的.
- application 在 application 对象内是单例
- global session spring 推 出 的 一 个 对 象 , 依 赖 于spring-webmvc-portlet,类似于 session
④单例设计模式
大佬说:单例设计模式
- 作用: 在应用程序有保证最多只能有一个实例.
- 好处:
- 提升运行效率.
- 实现数据共享. 案例:application 对象
- 懒汉式
- 对象只有被调用时才去创建
- 由于添加了锁,所以导致效率低
- 饿汉式
- 解决了懒汉式中多线程访问可能出现同一个对象和效率低问题
package com.tk.test;
/**
* 单例设计模式——懒汉式
* @data :2019-4-14下午12:58:35
* @author :田坤
*/
public class SingleTon {
private static SingleTon singleTon;
//该类不能被实例化
private SingleTon(){
}
//获取SingleTon对象 因为不能被实例化,所以只能从静态方法获取
public static SingleTon getInstance(){
if(singleTon == null){
/**
* 在多线程访问的情况下,可能会出现if用时成立的情况,添加锁
* 并且进行双重判断
* DCL双检查锁机制(DCL:double checked locking)
*/
synchronized (SingleTon.class) {
if(singleTon == null){
singleTon = new SingleTon();
}
}
}
return singleTon;
}
}
package com.tk.test;
/**
* 单例设计模式——饿汉式
* @data :2019-4-14下午12:58:35
* @author :田坤
*/
public class SingleTon {
//在类加载时就被实例化
private static SingleTon singleTon = new SingleTon();
//该类不能被实例化
private SingleTon(){
}
//获取SingleTon对象 因为不能被实例化,所以只能从静态方法获取
public static SingleTon getInstance(){
return singleTon;
}
}
⑤声明式事务
- 编程式事务:
- 由程序员编程事务控制代码.
- OpenSessionInView 编程式事务
- 声明式事务:
- 事务控制代码已经由 spring 写好.程序员只需要声明出哪些方法需要进行事务控制和如何进行事务控制
- 声明式事务都是针对于 ServiceImpl 类下方法的
- 事务管理器基于通知(advice)的
- 在 spring 配置文件中配置声明式事务
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 事务管理 spring jdbc包中 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" ></property>
</bean>
<!-- 配置声明式事务 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!-- 设置那些方法有事务控制 -->
<!-- 方法以...开头的事务管理 -->
<tx:method name="sel*"/>
<tx:method name="del*"/>
<tx:method name="upd*"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<!-- 起点设置的范围大一点 -->
<aop:pointcut expression="execution(* com.tk.service.impl.*.*(..))" id="mypoint1"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="mypoint1"/>
</aop:config>
⑥声明式事务中的属性解释
- name=” ”哪些方法需要有事务控制
- 支持*通配符
- readonly=”boolean”是否是只读事务.
- 如果为 true,告诉数据库此事务为只读事务.数据化优化,会对性能有一定提升,所以只要是查询的方法,建议使用此数据
- 如果为 false(默认值),事务需要提交的事务.建议新增,删除,修改
- propagation 控制事务传播行为
- 当一个具有事务控制的方法被另一个有事务控制的方法调用后,需要如何管理事务(新建事务?在事务中执行?把事务挂起?报异
常?) - REQUIRED(默认值): 如果当前有事务,就在事务中执行,如果当前没有事务,新建一个事务.
- SUPPORTS:如果当前有事务就在事务中执行,如果当前没有事务,就在非事务状态下执行
- MANDATORY:必须在事务内部执行,如果当前有事务,就在事务中执行,如果没有事务,报错
- REQUIRES_NEW:必须在事务中执行,如果当前没有事务,新建事务,如果当前有事务,把当前事务挂起
- NOT_SUPPORTED:必须在非事务下执行,如果当前没有事务,正常执行,如果当前有事务,把当前事务挂起
- NEVER:必须在非事务状态下执行,如果当前没有事务,正常执行,如果当前有事务,报错
- NESTED:必须在事务状态下执行.如果没有事务,新建事务,如果
当前有事务,创建一个嵌套事务
- isolation=”” 事务隔离级别
- 在多线程或并发访问下如何保证访问到的数据具有完整性的
- 脏读:
- 一个事务(A)读取到另一个事务(B)中未提交的数据,另一个事务中数据可能进行了改变,此时A事务读取的数据可能和数据
库中数据是不一致的,此时认为数据是脏数据,读取脏数据过程叫做脏读
- 不可重复读:
- 主要针对的是某行数据.(或行中某列)
- 主要针对的操作是修改操作.
- 两次读取在同一个事务内
- 当事务A第一次读取事务后,事务B对事务A读取的数据进行修改,事务 A 中再次读取的数据和之前读取的数据不一致,过
程不可重复读
- 幻读:
- 主要针对的操作是新增或删除
- 两次事务的结果
- 事务A按照特定条件查询出结果,事务B新增了一条符合条件的数据.事务 A 中查询的数据和数据库中的数据不一致的,事
务 A 好像出现了幻觉,这种情况称为幻读.
- DEFAULT: 默认值,由底层数据库自动判断应该使用什么隔离界别
- READ_UNCOMMITTED: 可以读取未提交数据,可能出现脏读,不重复读,幻读.
- 效率最高
- READ_COMMITTED:只能读取其他事务已提交数据.可以防止脏读,可能出现不可重复读和幻读
- REPEATABLE_READ: 读取的数据被添加锁,防止其他事务修改此数据,可以防止不可重复读.脏读,可能出现幻读
- SERIALIZABLE: 排队操作,对整个表添加锁.一个事务在操作数据时,另一个事务等待事务操作完成后才能操作这个表
- 最安全的
- 效率最低的
- rollback-for=”异常类型全限定路径”
- 当出现什么异常时需要进行回滚
- 建议:给定该属性值.
- 手动抛异常一定要给该属性值
- no-rollback-for=””
- 当出现什么异常时不滚回事务.
⑦Spring中常用注解
- @Component 创建类对象,相当于配置<bean/>
- @Service 与@Component 功能相同.
- 写在 ServiceImpl 类上.
- @Repository 与@Component 功能相同.
- 写在数据访问层类上.
- @Controller 与@Component 功能相同.
- 写在控制器类上.
- @Resource(不需要写对象的 get/set)
- java 中的注解
- 建议把对象名称和 spring 容器中对象名相同
- @Autowired(不需要写对象的 get/set)
- spring 的注解
- 默认按照 byType 注入.
- @Value() 获取 properties 文件中内容
- @Pointcut() 定义切点
- @Aspect() 定义切面类
- @Before() 前置通知
- @After 后置通知
- @AfterReturning 后置通知,必须切点正确执行
- @AfterThrowing 异常通知
- @Arround 环绕通知
⑧Ajax
重定向和超链接都是俩次请求
①客户端向服务器发送要请求的地址 ②服务器返回你允许请求这个地址 ③客户端去请求这个地址 ④服务器返回
1、先进行预处理,判断允不允许资源存不存在
能力提升:
一、利用验证码登录
1、在web.xml配置 和 applicationContext.xml的配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name></display-name>
<!-- 当tomcat加载web.xml 把spring配置信息存储在application对象 -->
<!-- WebApplicationContext 容器,是ApplicationContext的子接口 -->
<!-- 设置spring配置文件路径 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<description>This is the description of my J2EE component</description>
<display-name>This is the display name of my J2EE component</display-name>
<servlet-name>Login</servlet-name>
<servlet-class>com.tk.servlet.Login</servlet-class>
</servlet>
<servlet>
<description>This is the description of my J2EE component</description>
<display-name>This is the display name of my J2EE component</display-name>
<servlet-name>CheckCode</servlet-name>
<servlet-class>com.tk.servlet.CheckCode</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Login</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>CheckCode</servlet-name>
<url-pattern>/checkCode</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
<?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="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/rejava?useUnicode=true&characterEncoding=utf8"></property>
<property name="username" value="root"></property>
<property name="password" value="1327148745"></property>
</bean>
<!-- spring帮助创建sqlsessionfactory -->
<bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 扫描器 扫描接口并创建接口对象 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.tk.mapper"></property>
<property name="sqlSessionFactory" ref="factory"></property>
</bean>
<bean id="userService" class="com.tk.service.impl.UserServiceImpl">
<property name="userMapper" ref="userMapper"></property>
</bean>
</beans>
2、实体类(数据库存储的格式如此)
package com.tk.pojo;
public class User {
private int id;
private String name;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", password=" + password
+ "]";
}
}
3、数据库访问层
public interface UserMapper {
@Select("select * from s_user where name=#{name} and password=#{password}")
User checkLogin(User u);
}
4、业务层(service + serviceImpl)
package com.tk.service;
import com.tk.pojo.User;
public interface UserService {
/**
* 根据用户名密码验证登录是否成功
* @param u
* @return
*/
User CheckLogin(User u);
}
package com.tk.service.impl;
import com.tk.mapper.UserMapper;
import com.tk.pojo.User;
import com.tk.service.UserService;
public class UserServiceImpl implements UserService{
private UserMapper userMapper;
public UserMapper getUserMapper() {
return userMapper;
}
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
@Override
public User CheckLogin(User u) {
System.out.println(u);
return userMapper.checkLogin(u);
}
}
5、控制层(servlet)
生成验证码
package com.tk.servlet;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* 验证码的生成
* @data :2019-4-13下午12:15:57
* @author :田坤
*/
public class CheckCode extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//创建一张图片 单位:像素
BufferedImage bi = new BufferedImage(200, 100,BufferedImage.TYPE_INT_BGR);
//透明的玻璃
//向画板画内容必须先设置画笔画笔
Graphics2D graphics = bi.createGraphics();
graphics.setColor(Color.white);
//从哪个坐标开始填充,后俩个参数是区域
graphics.fillRect(0, 0, 200, 100);
//设置验证码 随机
Random random = new Random();
List<Integer> lists = new ArrayList<Integer>();
for (int i = 0; i < 4; i++) {
lists.add(random.nextInt(10));
}
//设置为黑色画笔
graphics.setColor(Color.black);
//设置一个Color数组
Color[] colors =new Color[] {Color.BLACK, Color.BLUE,Color.RED,Color.GRAY,Color.GREEN,Color.YELLOW,Color.PINK,Color.ORANGE,Color.CYAN};
//设置像素 多种样式用 | 隔开
graphics.setFont(new Font("宋体", Font.BOLD|Font.ITALIC, 50));
for (int i = 0; i < 4; i++) {
graphics.setColor(colors[random.nextInt(6)]);
graphics.drawString(lists.get(i)+"",i*40,70+random.nextInt(41)-20);
}
//画干扰线
for (int i = 0; i < 4; i++) {
graphics.setColor(colors[random.nextInt(6)]);
graphics.drawLine(random.nextInt(21), random.nextInt(101), 200-random.nextInt(21), random.nextInt(101));
}
/* //输入文字流
PrintWriter writer = resp.getWriter();*/
//获取响应流
ServletOutputStream outputStream = resp.getOutputStream();
//工具类
ImageIO.write(bi,"jpg", outputStream);
//因为我们需要验证验证码是否正确,把验证码放在session里面
HttpSession hs = req.getSession();
hs.setAttribute("ckCode", ""+lists.get(0)+lists.get(1)+lists.get(2)+lists.get(3));
}
}
package com.tk.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.tk.pojo.User;
import com.tk.service.UserService;
import com.tk.service.impl.UserServiceImpl;
/**
* 根据用户输入的用户名和密码判断其是否正确
* @data :2019-4-13下午12:18:01
* @author :田坤
*/
public class Login extends HttpServlet {
private UserService us;
@Override
public void init() throws ServletException {
WebApplicationContext ac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
us = ac.getBean("userService",UserServiceImpl.class);
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
String name = req.getParameter("username");
String password = req.getParameter("password");
String code = req.getParameter("checkCode");
//获取在session中存储的正确验证码
HttpSession hs = req.getSession();
String truecode = (String) hs.getAttribute("ckCode");
if(truecode.equals(code)){
User user = new User();
user.setName(name);
user.setPassword(password);
user = us.CheckLogin(user);
System.out.println(user);
if(user!=null){
//成功
resp.sendRedirect("main.jsp");
}else{
req.setAttribute("error", "用户名或者密码错误");
req.getRequestDispatcher("index.jsp").forward(req, resp);
}
}else{
req.setAttribute("error", "验证码错误");
req.getRequestDispatcher("index.jsp").forward(req, resp);
}
}
}
6、视图层
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<script type="text/javascript" src="js/jquery-1.7.2.js"></script>
<script type="text/javascript">
$(function(){
$("a").click(function(){
//浏览器带有缓存功能,不会多次去请求相同的数据
//解决:给每个请求url加上当前的时间戳
$("img").attr("src","checkCode?"+new Date());
return false;
})
});
</script>
<body>
${error }
<form action="login" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
验证码:<input type="text" name="checkCode"><img alt="check coode" src="checkCode" width="120" height="60">
<a href="">看不清</a><br>
<input type="submit" value="登录">
</form>
</body>
</html>
main.jsp页面没有任何东西,只是为了校验是否成功登陆