调整项目环境
package cn.ken.virtual_ioc;
/**
* myBean对象
* 用来接收配置文件中bean标签的id和class属性值
*/
public class MyBean {
private String id;
private String clazz;
public MyBean(String id, String clazz){
this.id = id;
this.clazz = clazz;
}
public MyBean() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getClazz() {
return clazz;
}
public void setClazz(String clazz) {
this.clazz = clazz;
}
}
用于解析配置文件
<dependency>
<groupId>dom4jgroupId>
<artifactId>dom4jartifactId>
<version>1.6.1version>
dependency>
<dependency>
<groupId>jaxengroupId>
<artifactId>jaxenartifactId>
<version>1.1.6version>
dependency>
<beans>
<bean id="user" class="cn.ken.pojo.User"/>
beans>
package cn.ken.virtual_ioc;
/**
* 工厂模式
* 自定义工厂方法
*/
public interface MyFactory {
// 通过id属性值获取实例化对象
public Object getBean(String id);
}
package cn.ken.virtual_ioc;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 工厂接口实现类
* 1.通过构造器的形参,传递要解析的配置文件
* 2.解析配置文件,得到对应的bean标签的id与class属性值,并设置到对应的myBean对象中,存放到list集合里
* 3.通过遍历list集合,得到每个myBean对象,从中得到属性对应的实例化对象,存放到map集合,通过id与实例化对象
* 4.通过getBean方法,从map对象中通过id获取对应的value,即实例化对象
*/
public class MyClassPathXmlApplicationContext implements MyFactory{
//定义map对象,用于存放id属性与对应的class属性实例好的bean对象
private Map<String, Object> beanMap = new HashMap<>();
//定义集合,用来存放myBean对象(myBean是用来存放配置文件中bean标签对应的id与class属性值)
private List<MyBean> beanList = null;
/**
* 1.通过构造器的形参,传递要解析的配置文件
* @param fileName
*/
public MyClassPathXmlApplicationContext(String fileName) {
//解析配置文件
parseXml(fileName);
//实例化对象
instanceBean();
}
/**
* 2.解析配置文件,得到对应的bean标签的id与class属性值,并设置到对应的myBean对象中,存放到list集合里
* @param fileName
*/
private void parseXml(String fileName) {
try{
//得到解析器
SAXReader saxReader = new SAXReader();
//得到配置文件对应的url
URL url = saxReader.getClass().getClassLoader().getResource(fileName);
//解析配置文件,得到Document对象
Document document = saxReader.read(url);
//XPath使用路径表达式来选取XML文档中的节点或节点集
//定义XPath语法,获取beans标签下所有的bean标签
XPath xPath = document.createXPath("beans/bean");
//通过XPath语法,得到对应的bean标签,返回一个Element集合
List<Element> elementList = xPath.selectNodes(document);
//判断element集合是否为空
if(elementList != null && elementList.size() > 0){
beanList = new ArrayList<>();
//遍历Element集合,得到Element对象,得到对应的属性值
for(Element element : elementList){
String id = element.attributeValue("id");
String clazz = element.attributeValue("class");
MyBean myBean = new MyBean(id,clazz);
beanList.add(myBean);
}
}
} catch (Exception e){
e.printStackTrace();
}
}
/**
* 3.通过遍历list集合,得到每个myBean对象,从中得到属性对应的实例化对象,存放到map集合,通过id与实例化对象
*/
private void instanceBean() {
try{
if(beanList != null && beanList.size() > 0){
for(MyBean myBean : beanList){
String id = myBean.getId();
String clazz = myBean.getClazz();
//通过反射,实例化指定的class属性值对应的Bean对象
Object object = Class.forName(clazz).newInstance();
beanMap.put(id,object);
}
}
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 4.通过getBean方法,从map对象中通过id获取对应的value,即实例化对象
* @param id
* @return
*/
@Override
public Object getBean(String id) {
//通过id属性,从map中获取对应的实例化对象
Object object = beanMap.get(id);
return object;
}
}
通过默认构造器创建,空构造方法必须存在,否则创建失败
配置文件
<bean id="userService" class="cn.ken.factory.StaticFactory" factory-method="createUserService">bean>
定义静态工厂类,类中定义静态方法,方法返回实例化的Bean对象
id:需要被实例化的Bean对象的id
class:静态工厂类的类路径
factory-method:静态工厂类中对应的静态方法
当我们指定Spring静态工厂方法来创建Bean实例时,Spring将先解析配置文件,并根据配置文件指定的信息,通过反射调用静态工厂类的静态工厂方法,并将该静态工厂方法的返回值作为Bean实例,在这个过程中,Spring将不再负责创建Bean对象,Bean实例的创建交给用户提供的静态工厂方法。
工厂方法为非静态方法
需要配置工厂Bean,并在业务Bean中配置factory-bean,factory-method属性
<bean id="instanceFactory" class="cn.ken.factory.InstanceFactory">bean>
<bean id="userService" factory-bean="instanceFactory" factory-method="createUserService">bean>
定义工厂类,类中定义普通方法,方法返回实例化的Bean对象
factory-bean:对应工厂类的bean标签的id属性值
factory-method:工厂类中的方法
<bean id="userDao" class="cn.ken.dao.impl.UserDaoImpl">bean>
<bean id="userService" class="cn.ken.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao">property>
bean>
@Autowired注解会按照数据类型从Spring容器中自动进行匹配,如果UserDao只有一个实现的Bean,则@Qualifier注解可以省略
即@Autowired是按照类型自动匹配的,@Qualifier是按照id进行匹配的(当有多个实现的Bean时就需要加上这个注解来选择使用哪个Bean注入,且此注解不能单独使用,得配合@Autowired一起使用)
当需要通过id匹配时也可以直接使用@Resource注解,则直接写@Resource(name="userDao")
就行(相当于@Autowired + @Qualifier(“userDao”))
如果使用注解配置可以不用set方法,注解标志处会通过暴力反射直接为该field赋值,不需要set方法
<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="cn.ken"/>
beans>
默认情况下,我们从Spring容器中拿到的对象均是单例的,对于bean的作用域类型如下:
Singleton作用域
注意:lazy-init是懒加载,如果等于true时作用是指Spring容器启动时不回去实例化这个bean,而是在程序去调用时才去实例化,默认是false即Spring容器启动时加载
默认情况下,被管理的bean只会在IOC容器中存在一个实例,对于所有获取该bean的操作Spring容器将只返回同一个bean
设置为lazy-init的好处:
什么对象适合作为单例对象
一般来说对于无状态或状态不可改变的对象适合使用单例模式(不存在会改变对象状态的成员变量比如Controller层、service层和dao层)。实际上对象状态的变化往往均是由属性值的变化而引起的,比如user类姓名属性会有变化,属性姓名的变化一般会引起user对象状态的变化。对于我们的程序来说,无状态对象没有实例变量的存在,保证了我们线程的安全,service层业务对象即是无状态对象,线程安全的。
prototype作用域
通过scope=“prototype”设置bean的类型,每次向Spring容器请求获取bean都返回一个全新的bean,相对于singleton来说就是不缓存bean,每次都是一个根据bean定义创建的全新的bean
bean的销毁
在项目开发中定时任务是一种比较常见的需求,主要用三种解决方案:一是使用JDK自带的Timer,二是使用第三方组件Quartz,第三是使用Spring Task
两种解决方法:
<task:scheduled-tasks>
<task:scheduled ref="taskJob" method="job1" cron="0/2 * * * * ?"/>
task:scheduled-tasks>
<task:annotation-driven/>
@Scheduled(cron = "2,4,6,10 * * * * *")
为某一个对象(委托类)提供一个代理(代理类),用来控制对这个对象的访问。委托类和代理类有一个共同的父类或父接口。代理类会对请求做预处理、过滤,将请求分配给指定对象。
代理模式有俩个设计原则:
代理类和委托类具有相似的行为
代理类增强委托类的行为
public class StaticsProxyStarter {
public static void main(String[] args) {
Marry people = new ToMarry();
//通过代理类完成委托类目标
MarryProxy proxy = new MarryProxy(people);
proxy.toMarry();;
}
}
//代理类
public class MarryProxy implements Marry {
private Marry people;
public MarryProxy(Marry people) {
this.people = people;
}
public void toMarry() {
//增强行为
System.out.println("场景布置");
System.out.println("音乐布置");
people.toMarry();//实现委托类目标行为
System.out.println("后续内容");
}
}
//委托类
public class ToMarry implements Marry {
public void toMarry() {
System.out.println("结婚");
}
}
//共同行为
public interface Marry {
void toMarry();
}
要素
特点
相比于静态代理,动态代理在创建代理对象上更加的灵活,动态代理类的字节码在程序的运行时,由Java的反射机制动态产生。它会根据需要,通过反射机制在程序运行期,动态的为目标对象创建代理对象,无需程序员手动编写它的源代码。动态代理不仅简化了编程工作,而且提高了软件系统的可拓展性,因为反射机制可以生成任意类型的动态代理类。它的行为可以代理多个方法,即满足生产需要的同时又达到代码通用的目的。
特点:
JDK动态代理的目标对象必须有接口实现
public class JdkProxy {
//目标对象
private Object target;
public JdkProxy(Object target) {
this.target = target;
}
public Object getProxy(){
ClassLoader classLoader = this.getClass().getClassLoader();
Class[] interfaces = target.getClass().getInterfaces();
InvocationHandler invocationHandler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("准备事宜");
Object result = method.invoke(target, args);
System.out.println("结婚后");
return result;
}
};
Object object = Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
return object;
}
}
public class JdkProxyStarter {
public static void main(String[] args) {
Marry marry = new ToMarry();
JdkProxy jdkProxy = new JdkProxy(marry);
Marry proxy = (Marry) jdkProxy.getProxy();
proxy.toMarry();
}
}
JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能用JDK动态代理,CGLIB是针对类来实现代理的(接口也可以),它的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,所以不能对final修饰的类进行代理。
添加依赖
<dependency>
<groupId>cglibgroupId>
<artifactId>cglibartifactId>
<version>3.3.0version>
dependency>
package cn.ken.start;
import cn.ken.common.Marry;
import cn.ken.common.User;
import cn.ken.cust.ToMarry;
import cn.ken.proxy.CGLibProxy;
public class CGLibProxyStarter {
public static void main(String[] args) {
// Marry marry = new ToMarry();
// CGLibProxy cgLibProxy = new CGLibProxy(marry);
// Marry proxy = (Marry) cgLibProxy.getProxy();
// proxy.toMarry();
User user = new User();
CGLibProxy cgLibProxy = new CGLibProxy(user);
User proxy = (User) cgLibProxy.getProxy();
proxy.test();
}
}
package cn.ken.proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CGLibProxy {
private Object target;
public CGLibProxy(Object target) {
this.target = target;
}
public Object getProxy(){
//通过Enhancer对象的creat方法可以生成一个类,用于生成代理对象
Enhancer enhancer = new Enhancer();
//设置当前类的父类(将目标类作为代理类的父类)
enhancer.setSuperclass(target.getClass());
//定义MethodInterpret方法拦截器
MethodInterceptor methodInterceptor = new MethodInterceptor() {
/**
*
* @param o 由CGLib动态生成的代理类实例
* @param method 实体类所调用的被代理的方法的引用
* @param objects 参数值列表
* @param methodProxy 生成的代理类对方法的代理引用
* @return
* @throws Throwable
*/
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before");
Object result = method.invoke(target, objects);
System.out.println("after");
return result;
}
};
//设置代理过程(调用目标对象方法,增强用户行为)
enhancer.setCallback(methodInterceptor);
//生成一个类
return enhancer.create();
}
}
AOP面向切面编程,相比较于oop面向对象编程来说,AOP关注的不再是程序代码中某个类、某个方法,而AOP考虑的更多是一种面到面的切入,即层与层之间的一种切入,所以称之为切面。
AOP主要应用于日志记录,性能统计,安全控制,事务处理等方面,实现公共功能性的重复使用。
AOP的特点
底层实现
动态代理(JDK+CGLIB)
概念
环境搭载
坐标依赖引入
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.5version>
dependency>
添加spring.xml的配置
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
<aop:aspectj-autoproxy/>
package cn.ken.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* 定义切面
* 切面是 切入点 与 通知 的结合
* 在切面类中定义切入点与通知:
* 切入点:定义规则,定义切面要拦截哪些类哪些方法
* 通知:方法拦截后要做什么事情
*/
@Component//交给IOC容器管理
@Aspect//声明切面类
public class LogCut {
/**
* 定义切入点
* 通过@Pointcut定义规则
* 1、拦截所有方法
* @Pointcut("execution(* *(..))")
* 2、拦截所有公用的set方法
* @Pointcut("execution(public set*(..))")
* 3、拦截service包下面的所有类的所有方法
* @Pointcut("execution(* cn.ken.service.*.*(..))")
* 4、拦截service包及其子包下的所有类的所有方法
* @Pointcut("execution(* cn.ken.service..*.*(..))")
* 规则表达式的第一个位置表示方法的修饰范围
*/
@Pointcut("execution(* cn.ken.service..*.*(..))")
public void cut(){}
/**
* 前置通知 应用在指定切入点上
* 在目标方法执行之前
*/
@Before(value = "cut()")
public void before(){
// System.out.println("前置通知");
}
/**
* 返回通知 应用在指定切入点上
* 在目标方法无异常返回后执行
*/
@AfterReturning(value = "cut()")
public void afterReturning(){
// System.out.println("返回通知");
}
/**
* 最终通知 应用在指定切入点上
* 在目标方法执行后(无论是否出现异常)执行
*/
@After(value = "cut()")
public void after(){
// System.out.println("最终通知");
}
/**
* 异常通知 应用在指定切入点上
* 在目标方法执行异常时执行
*/
@AfterThrowing(value = "cut()", throwing = "e")
public void afterThrowing(Exception e){
// System.out.println("异常通知,原因:" + e.getMessage());
}
/**
* 环绕通知 应用在指定切入点上
* 方法执行前后 通过环绕通知定义相应处理
* 需要通过显示调用对应的方法,否则无法访问指定的方法(proceedingJoinPoint.proceed();)
* @param proceedingJoinPoint
* @return
*/
@Around(value = "cut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint){
System.out.println("前置通知");
Object result = null;
try{
result = proceedingJoinPoint.proceed();
System.out.println("返回通知");
} catch (Throwable e) {
System.out.println("异常通知");
e.printStackTrace();
} finally {
System.out.println("最终通知");
}
return result;
}
}
public class AopStarter {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.test();
userService.test1();
}
}
@Service
public class UserService {
@Resource
private UserDao userDao;
public void test(){
System.out.println("service...");
}
public void test1(){
int i = 1/0;
System.out.println("service...");
}
}
LogCut类只需要@Component注解不需要@Aspect注解
<aop:config>
<aop:aspect ref="logCut">
<aop:pointcut id="cut" expression="execution(* cn.ken.service..*.*(..))"/>
<aop:before method="before" pointcut-ref="cut"/>
<aop:after method="after" pointcut-ref="cut"/>
<aop:around method="around" pointcut-ref="cut"/>
<aop:after-throwing method="afterThrowing" throwing="e" pointcut-ref="cut"/>
aop:aspect>
aop:config>
maven配置文件
<build>
<resources>
<resource>
<directory>src/main/resourcesdirectory>
resource>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.xmlinclude>
<include>**/*.propertiesinclude>
<include>**/*.tldinclude>
includes>
<filtering>falsefiltering>
resource>
resources>
build>
spring配置文件
<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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="cn.ken"/>
<aop:aspectj-autoproxy/>
<context:property-placeholder location="db.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
bean>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
tx:attributes>
tx:advice>
<aop:config>
<aop:pointcut id="servicePointcut" expression="execution(* cn.ken.service..*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="servicePointcut"/>
aop:config>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis.xml"/>
<property name="mapperLocations" value="classpath:cn/ken/mapper/*.xml"/>
bean>
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.ken.dao"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
bean>
beans>
mybatis配置文件
DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="cn.ken.pojo"/>
typeAliases>
configuration>
数据库配置文件
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mgsql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
jdbc.username=root
jdbc.password=129496
mapper配置文件
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper>
mapper>
xml配置事务
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
tx:attributes>
tx:advice>
<aop:config>
<aop:pointcut id="servicePointcut" expression="execution(* cn.ken.service..*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="servicePointcut"/>
aop:config>
注解配置事务
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
<tx:annotation-driven transaction-manager="txManager"/>
//在需要添加事务的方法上加入事务注解
@Transactional(propagation = Propagation.REQUIRED)
//如果有事务则加入事务,如果没有则新建事务
两种配置事务的方法可以同时使用,相辅相成
默认spring事务只在发生未被捕获的runtimeException时才回滚
被拦截的方法需要显示抛出异常,并且不能经过任何处理,这样aop代理才能捕获到方法的异常,才能进行回滚
模型-视图-控制器(MVC)是一个以设计页面应用程序为基础的设计思想。它主要通过分离模型、试图及控制器在应用程序的角色将业务逻辑从界面中解耦。通常模型负责封装应用程序数据在视图层展示。视图仅仅只是展示这些数据,不包含任何业务逻辑。控制器负责接收来自用户的请求,并调用后台服务(service或者dao)来处理业务逻辑。处理后,后台业务层可能会返回了一些数据在视图层展示。控制器收集这些数据以及准备模型在视图层展示。MVC模式的核心思想是将业务逻辑从界面中抽离出来,允许他们单独改变而不会相互影响。
是一个基于请求驱动的Web框架
servlet-context.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="cn.ken.controller"/>
<mvc:annotation-driven/>
<mvc:default-servlet-handler/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
bean>
beans>
web.xml
DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Applicationdisplay-name>
<filter>
<filter-name>encodingFilterfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>UTF-8param-value>
init-param>
filter>
<filter-mapping>
<filter-name>encodingFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
<servlet>
<servlet-name>springMvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:servlet-context.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springMvcservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
web-app>
EL表达式失效
这个问题是由于 版本 太高导致的,在 JSP2.0 时,增加了 EL 语言,我们可以通过 EL 语言,实现获取数据,进一步将 Servlet 的代码从 JSP 页面中分离出来。而 EL 语言给大家带来了方便。但有时也会遇到 EL 表达式不能显示的情况。
在 2.0 到 2.4 版本它是默认支持 EL 表达式的,如果使用 2.5 版本,默认 EL 表达式是关闭的,我们需要开启才可以使用。
解决方法:
换成 2.0 ~ 2.4 之间的版本或在页面中加入<%@ page isELIgnored=“false” %>
/**
* 重定向与请求转发
* SpringMVC内部默认采用请求转发形式
* 请求转发
* 地址栏不发生改变,以forward: 开头
* 方法有两种返回值
* 1、返回ModelAndView对象
* 2、返回视图名称字符串
* 注:默认都会从视图解析器设置的路径下查找指定视图(不需要设置视图后缀,直接写视图名),如果想从项目的根目录(如果有类路径,则从类路径)查找,则使用forward:
* 重定向
* 地址栏会发生改变,以redirect: 开头
*/
@Controller
@RequestMapping("user")
public class UserController {
@RequestMapping("view")
public ModelAndView hello(@RequestParam(name = "uid", defaultValue = "11")Integer id){
ModelAndView modelAndView = new ModelAndView();
//设置数据模型
modelAndView.addObject("key","value");
//设置试图
modelAndView.setViewName("hello");
System.out.println(id);
return modelAndView;
}
@RequestMapping("view1")
public String view1(){
return "forward:/WEB-INF/jsp/view1.jsp";
}
//转发至控制器需加forward,不用forward被视图解析器加前后缀则无法转发到控制器
@RequestMapping("view2")
public String view2(){
return "forward:view?uid=222";
}
}
<%--
Created by IntelliJ IDEA.
User: Ken-Chy
Date: 2022/2/23
Time: 22:45
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false" %>
Title
${key}
${msg}
${param.uid}
重定向是发一个302状态码给浏览器,浏览器自己去请求跳转的网页,地址栏也会发生改变
/**
* 重定向:
* 默认从项目的根目录下查找资源(如果类设置了路径,则从类路径即user开始查找)
* @param session
* @return
*/
@RequestMapping("view3")
public String view3(HttpSession session){
//因为重定向是两次请求,故通过HttpServletRequest设置的值无法在重定向后使用,需要使用session设置值
session.setAttribute("msg","11211222");
return "redirect:view";
}
/**
* 重定向可以传递参数,如果直接传递中文,则无法获取(因为浏览器会将中文参数进行unicode编码)
* 故需要使用RedirectAttributes对象设置
* @param attributes
* @return
*/
@RequestMapping("view4")
public String view4(RedirectAttributes attributes){
//通过此种方法会自动在重定向时拼接上参数
attributes.addAttribute("name","张三");
//不需要自己在下面路径后用问号加参数
return "redirect:/view111.jsp";
}
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-annotationsartifactId>
<version>2.11.4version>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-coreartifactId>
<version>2.11.4version>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.11.4version>
dependency>
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">bean>
mvc:message-converters>
mvc:annotation-driven>
/**
* Json数据开发
* @ResponseBody:
* 设置在方法级别或者方法的类型前
* 默认控制器中方法的返回值是会去找对应的视图页面,如果想要返回数据,需要将返回的结果转换成字符串响应(加上JsonConverter
* 则转换成json字符串)
* @RequestBody:
* 设置在形参前
* 要求传递的参数是一个Json格式的字符串(字符串!!!)
*/
@Controller
@RequestMapping("json")
public class JsonController {
@RequestMapping("json1")
@ResponseBody
public User json(){
User user = new User("xxx","ssss");
return user;
}
@RequestMapping("json2")
@ResponseBody
public User json2(User user){
return user;
}
}
Hello World!
<mvc:interceptors>
<bean class="cn.ken.MyInterceptor">bean>
mvc:interceptors>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/json/*"/>
<bean class="cn.ken.MyInterceptor">bean>
mvc:interceptor>
mvc:interceptors>
beans>
实现方法一:实现HandlerInterceptor接口
public class MyInterceptor implements HandlerInterceptor {
//在目标方法(Handler)执行之前执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("11");
return true;
}
//在目标方法实行之后,视图生成之前
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("22");
}
//在视图生成之后执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("33");
}
}
实现方法二:继承HandlerInterceptorAdapter类
如果多个拦截器同时生效,则先配置的先执行
先配置的拦截其中对应的preHandle方法限制性,postHandle和afterCompletion后执行
<dependency>
<groupId>commons-fileuploadgroupId>
<artifactId>commons-fileuploadartifactId>
<version>1.3.2version>
dependency>
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">
<property name="maxUploadSize">
<value>104857600value>
property>
<property name="maxInMemorySize">
<value>4096value>
property>
bean>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
<%--
文件上传表单:
表单的提交方式:post
表单的类型:普通表单,二进制表单(enctype="multipart/form-data")
设置文件域的name属性值
--%>
@Controller
public class UploadController {
/**
* 文件上传
* 在形参上使用@RequestParam("myfile")MultipartFile file接收上传的文件
* 其中myfile代表的是表单元素的name属性值
*/
@RequestMapping("upload")
public String upload(HttpServletRequest request, @RequestParam("myfile")MultipartFile file){
if (file.isEmpty()){
request.setAttribute("msg","请上传文件");
return "result";
}
//得到上传的文件名
String name = file.getOriginalFilename();
//截取文件名获得后缀
String siffx = name.substring(name.lastIndexOf("."));
//生成随机文件名
String filename = System.currentTimeMillis() + siffx;
//得到项目的真实路径
String realPath = request.getServletContext().getRealPath("/");
//设置文件的存放路径
File filePath = new File(realPath+"/upload");
//判断文件是否存在
if (!filePath.exists()){
filePath.mkdir();
}
try {
file.transferTo(new File(filePath, filename));
request.setAttribute("msg","文件上传成功");
} catch (IOException e){
request.setAttribute("msg","文件上传失败");
e.printStackTrace();
}
return "result";
}
}
Restful风格的API是一种软件架构风格,设计风格而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件,基于这个设计风格设计的软件可以更简洁、更有层次,更易于实现缓存等机制
在Restful风格中,用户请求的url使用同一个url,请求方式采用get、post、delete、put等方式对请求的处理方法进行分类,这样可以在前后台分离式的开发中使得前端开发人员不会对请求的资源地址产生混淆和大量额检查方法名的麻烦,形成一个统一的接口
/**
* Get请求:GetMapping("路径") 查询操作 == @RequestMapping(value="路径", method="RequestMethod.GET)
* Post请求:PostMapping("路径") 添加操作 == @RequestMapping(value="路径", method="RequestMethod.POST)
* Delete请求:DeleteMapping("路径") 删除操作 == @RequestMapping(value="路径", method="RequestMethod.DELETE)
* Put请求:PutMapping("路径") 修改操作 == @RequestMapping(value="路径", method="RequestMethod.PUT)
*
* 设置请求的参数:
* 1.路径参数:
* 在对应的注解上,设置路径时,在路径后面通过{}设置参数名,用于接受请求地址上的路径参数
* 在方法事务形参前设置@PathVariable注解,表示当前形参是一个路径参数(形参名和{}里对应的参数名应该一致)
* 2,Json格式的参数
* 使用RequestBody注解声明形参,表示该参数接受一个Json字符串的参数
* 3.表单参数
*
* 设置响应参数
* 响应Json格式的数据
* 使用@ResponseBody注解声明在方法级别,表示该方法返回Json格式的数据
*
* RESTFUL URL路径中不应该包含任何动作行为
*/
@Controller
public class UserController {
// @Autowired
// private UserService userService;
@GetMapping("user/{id}")
@ResponseBody
public User queryUser(@PathVariable Integer id){
System.out.println(id);
return new User();
}
@PostMapping("user")
@ResponseBody
public Map<String,Object> addUser(@RequestBody User user){
Map<String, Object> map = new HashMap<>();
// if(userService.addUser(user)>0){
// map.put("status","200");
// map.put("msg","添加成功");
// }
return map;
}
}
使用SpringMVC提供的简单异常处理器SimpleMappingExceptionResolver
使用简单,集成简单,扩展性好,对代码没有入侵性(即改变原来代码)
但仅能获取到异常信息,若在出现异常时,对需要获取异常以外的数据的情况不适用
@Service
public class UserServiceImpl implements UserService {
@Override
public int addUser(User user) {
// int i = 1/0; //-->error页面
if(false){
throw new BusinessException();
}
return 0;
}
public class BusinessException extends RuntimeException {
private Integer code=400;
private String msg="业务异常";
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="defaultErrorView" value="error"/>
<property name="exceptionAttribute" value="ex"/>
<property name="exceptionMappings">
<props>
<prop key="cn.ken.exception.BusinessException">buss_errorprop>
props>
property>
bean>
实现Spring的异常处理接口HandlerExceptionResolver自定义自己的异常处理器(推荐使用)
具有集成简单,有良好的扩展性,对代码没有入侵性
在异常处理时可以获取导致异常出现的对象,有利于提供更详细的异常处理信息
@Component
public class GlobalExceptionResolver implements HandlerExceptionResolver {
/**
*
* @param request
* @param response
* @param handler 目标方法
* @param ex 异常对象
* @return
*/
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
ModelAndView modelAndView = new ModelAndView("error");
modelAndView.addObject("ex","默认的异常信息");
/*
* 根据不同的异常执行不同的处理
*/
if (ex instanceof BusinessException){
BusinessException b = (BusinessException) ex;
b.setCode(500);
b.setMsg("这是业务异常");
modelAndView.setViewName("buss_error");
modelAndView.addObject("e",b);
return modelAndView;
}
return modelAndView;
}
}
使用@ExceptionHandler注解实现异常处理
具有集成简单,扩展性好(只需将需要异常处理的Controller类继承BaseController即可)不需要附加Spring配置等
但该方法对代码存在入侵性(需要修改已有代码,使相关类继承BaseController),在处理异常时不能获取除异常以外的数据
public class BaseController {
@ExceptionHandler
public String exceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception e){
request.setAttribute("ex","这是第三种异常处理");
if(e instanceof BusinessException){
return "buss_error";
}
return "error";
}
}
public class AccountController extends BaseController{
}
未捕获异常处理
在web.xml中通过结点配置特定异常情况的显示页面
<error-page>
<error-code>404error-code>
<location>/404.jsplocation>
error-page>
<error-page>
<exception-type>java.lang.Throwableexception-type>
<location>/500.jsplocation>
error-page>
<error-page>
<error-code>500error-code>
<location>/500.jsplocation>
error-page>
属于持久层ORM框架
遵循以下规范:
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.ken.dao.UserDao">
<sql id="selectUser">select * from usersql>
<select id="selectByConditions" parameterType="cn.ken.pojo.User" resultType="cn.ken.pojo.User">
<include refid="selectUser">include>
<where>
<if test="username!=null">
and username = #{username}
if>
<if test="id!=0">
and id = #{id}
if>
<if test="password!=null">
and password = #{password}
if>
where>
select>
<select id="selectByIds" parameterType="list" resultType="User">
<include refid="selectUser">include>
<where>
<foreach collection="list" item="id" open="id in(" close=")" separator=",">
#{id}
foreach>
where>
select>
mapper>
TypeHandlers标签(类型处理器)
你可以重写类型处理器或创建你自己的类型处理器(如把java的Date类型转换为毫秒值存储到数据库的BIGINT类型),具体做法为:实现TypeHandler接口或继承BaseTaypeHandler类,然后可以选择性地将它映射到一个JDBC类型。
开发步骤:
<typeHandlers>
<typeHandler handler="cn.ken.handler.DateTypeHandler"/>
typeHandlers>
public class DateTypeHandler extends BaseTypeHandler<Date> {
//将java类型转换为数据库类型
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException {
long time = date.getTime();
preparedStatement.setLong(i, time);
}
//根据名称将数据库中的类型转换java类型
@Override
public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {
long aLong = resultSet.getLong(s);
return new Date(aLong);
}
//根据列数
@Override
public Date getNullableResult(ResultSet resultSet, int i) throws SQLException {
long along = resultSet.getLong(i);
return new Date(along);
}
//存储过程
@Override
public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
long along = callableStatement.getLong(i);
return new Date(along);
}
}
plugins标签
MyBatis可以使用第三方插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据
开发步骤:
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelperartifactId>
<version>5.2.0version>
dependency>
<dependency>
<groupId>com.github.jsqlparsergroupId>
<artifactId>jsqlparserartifactId>
<version>3.2version>
dependency>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="helperDialect" value="mysql"/>
plugin>
plugins>
//设置分页相关参数,当前页+每页显示条数
PageHelper.startPage(2,3);
List<User> userList = mapper.findAll();
//获得与分页相关参数
PageInfo<User> pageInfo = new PageInfo<User>(userList);
pageInfo.getPageNum();
pageInfo.getPageSize();
pageInfo.getTotal();
pageInfo.getPages();
pageInfo.isFirstPage();
pageInfo.isLastPage();
一对一
<mapper namespace="cn.ken.dao.OrderDao">
<resultMap id="orderMap" type="cn.ken.pojo.Order">
<id column="oid" property="id"/>
<result column="name" property="name"/>
<association property="user" javaType="cn.ken.pojo.User">
<id column="uid" property="id"/>
<result column="username" property="username"/>
<result column="passwird" property="password"/>
association>
resultMap>
<select id="selectAll" resultMap="orderMap">
SELECT *, o.id oid FROM `orders` o,`user` u WHERE o.uid = u.id
select>
public class Order {
private Long id;
private String name;
//连接通过对象而不是uid
private User user;
}
一对多
<mapper namespace="cn.ken.dao.UserDao">
<resultMap id="userMap" type="cn.ken.pojo.User">
<id column="uid" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<collection property="orderList" ofType="cn.ken.pojo.Order">
<id column="oid" property="id"/>
<result column="name" property="name"/>
collection>
resultMap>
<select id="selectAll" resultMap="userMap">
select *, o.id oid from user u, orders o where u.id = o.uid
select>
public class User {
private Long id;
private String username;
private String password;
private List<Order> orderList;
}
多对多(建立中间表)
<resultMap id="roleMap" type="cn.ken.pojo.User">
<id column="uid" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<collection property="orderList" ofType="cn.ken.pojo.Role">
<id column="roleId" property="id"/>
<result column="roleName" property="roleName"/>
<result column="roleDesc" property="roleDesc"/>
collection>
resultMap>
<select id="selectAllRole" resultMap="roleMap">
select * from user u, role r, user_role ur, where u.id = ur.userId and r.id = ur.roleId
select>
r` u WHERE o.uid = u.id
```java
public class Order {
private Long id;
private String name;
//连接通过对象而不是uid
private User user;
}
一对多
<mapper namespace="cn.ken.dao.UserDao">
<resultMap id="userMap" type="cn.ken.pojo.User">
<id column="uid" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<collection property="orderList" ofType="cn.ken.pojo.Order">
<id column="oid" property="id"/>
<result column="name" property="name"/>
collection>
resultMap>
<select id="selectAll" resultMap="userMap">
select *, o.id oid from user u, orders o where u.id = o.uid
select>
public class User {
private Long id;
private String username;
private String password;
private List<Order> orderList;
}
多对多(建立中间表)
<resultMap id="roleMap" type="cn.ken.pojo.User">
<id column="uid" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<collection property="orderList" ofType="cn.ken.pojo.Role">
<id column="roleId" property="id"/>
<result column="roleName" property="roleName"/>
<result column="roleDesc" property="roleDesc"/>
collection>
resultMap>
<select id="selectAllRole" resultMap="roleMap">
select * from user u, role r, user_role ur, where u.id = ur.userId and r.id = ur.roleId
select>