
public class UserController extends HttpServlet { private UserService userService; …………}<bean id="userService" class="UserService"> …………</bean><bean id="userController" class="UserController"> <property name="userService" ref="userService"/> …………</bean><!-- context --><dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.6.RELEASE</version></dependency>public interface EmailService { void sendEmail();}public class EmailServiceImpl implements EmailService { public void sendEmail() { System.out.println("==== Send email ===="); }}public interface UserService { void register(String email, String password);}public class UserServiceImpl implements UserService { private EmailService emailService; // 通过 set 方法注入 emailService public void setEmailService(EmailService emailService) { this.emailService = emailService; } public void register(String email, String password) { System.out.println("==== Register user ===="); emailService.sendEmail(); }}<?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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 每个 bean 都有一个 id 标识,class 指向我们写的 bean 类全路径名 --> <bean id="emailService" class="com.sfac.spring.service.impl.EmailServiceImpl" /> <bean id="userService" class="com.sfac.spring.service.impl.UserServiceImpl"> <!-- 通过 property 注入另外一个 bean --> <property name="emailService" ref="emailService" /> </bean></beans>EmailService emailService = new EmailServiceImpl();UserService userService = new UserServiceImpl();userService.setEmailService(emailService);public class JavaSpringApplication { public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml"); UserService userService = applicationContext.getBean(UserServiceImpl.class); userService.register("hujiang", "111111"); }}public class EmailServiceImpl implements EmailService {}public class UserServiceImpl implements UserService { private EmailService emailService;}public class SpringIocTest { public static void main(String[] args) { // 通过读取xml的方式,ClassPath,对应到 target 下 classes 文件夹中的内容 // ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml"); // 通过注解的方式装配 Bean ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringIocTest.class); UserService userService = applicationContext.getBean(UserService.class); userService.register("admin1@163.com", "111111"); }}
public class MailService { …………}(ConfigurableBeanFactory.SCOPE_PROTOTYPE) // @Scope("prototype")public class MailService { …………}public class SpringIocTest { // 指定 Bean 别名的方式二,@Bean + @Qualifier //@Bean //@Qualifier("utc8") ZoneId zoneIdShanghai() { return ZoneId.of("UTC+08:00"); } // 指定 Bean 别名的方式一 ("utc") ZoneId zoneIdBase() { return ZoneId.of("UTC+00:00"); } …………}// 按照 bean 类型注入,找不到则忽略(required = false)private ZoneId zoneId = ZoneId.systemDefault();// 按照 bean 名字注入(name = "utc8")private ZoneId zoneIdUtc8;public interface Validator { void validate(String email, String password);}// @Order 为多个 Bean 设置优先级(1)public class EmailValidator implements Validator { public void validate(String email, String password) { if (!email.matches("^[a-z0-9]+\\@[a-z0-9]+\\.[a-z]{2,10}$")) { throw new IllegalArgumentException("invalid email: " + email); } }}(2)public class PasswordValidator implements Validator { public void validate(String email, String password) { if (!password.matches("^.{6,20}$")) { throw new IllegalArgumentException("invalid password"); } }}public class Validators { private List<Validator> list; public void validate(String email, String password) { for (Validator validator : list) { validator.validate(email, password); } }}jdbc.driverClassName=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=falsejdbc.username=rootjdbc.password=rootpublic class ConfigUtil { ("classpath:/jdbc-parms.properties") // classpath 方式 //@Value("file:/temp/jdbc-parms.properties") // 指定文件路径的方式 private Resource resource; public Properties getProperties() { Properties properties = new Properties(); try { properties.load(resource.getInputStream()); } catch (IOException e) { e.printStackTrace(); } return properties; }}Properties properties = configUtil.getProperties();System.out.println(properties.getProperty("jdbc.url"));("classpath:jdbc-parms.properties")public class JdbcParams { // 如果 jdbc.driverClassName 不存在,则使用冒号后面的默认值,默认值无需加引号 ("${jdbc.driverClassName:defaultValue}") private String driverClassName; ("${jdbc.url}") private String url; ("${jdbc.username}") private String userName; ("${jdbc.password}") private String password; …………}// UserServiceprivate JdbcParams jdbcParams;System.out.println(jdbcParams.getDriverClassName());("!dev") // 非 dev 环境创建ZoneId zoneIdShanghai() { return ZoneId.of("UTC+08:00");}("utc")("dev") // dev 环境创建ZoneId zoneIdBase() { return ZoneId.of("UTC+00:00");}//@Conditional(***.class)public class ConfigUtil {}// 前三个阶段容纳到 AbstractAutowireCapableBeanFactory doCreateBean 方法中protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException { // Instantiate the bean. BeanWrapper instanceWrapper = null; if (instanceWrapper == null) { // 实例化阶段 instanceWrapper = createBeanInstance(beanName, mbd, args); } // Initialize the bean instance. Object exposedObject = bean; try { // 属性赋值阶段 populateBean(beanName, mbd, instanceWrapper); // 初始化阶段 exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { } return exposedObject;}
// AbstractAutowireCapableBeanFactory.classprotected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException { try { // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. // postProcessBeforeInstantiation 方法调用点 // for循环调用所有的InstantiationAwareBeanPostProcessor // 在 doCreateBean 方法之前调用 // 该方法的返回值会替换原本的Bean作为代理,这也是Aop等功能实现的关键点 Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } } catch (Throwable ex) { } try { Object beanInstance = doCreateBean(beanName, mbdToUse, args); if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { }}// AbstractAutowireCapableBeanFactory.classprotected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) { // postProcessAfterInstantiation 调用点 // 方法作为属性赋值的前置检查条件,在属性赋值之前执行 // 返回值为boolean,返回 false 时可以阻断属性赋值阶段 boolean continueWithPropertyPopulation = true; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { continueWithPropertyPopulation = false; break; } } } }}// AbstractApplicationContext.classpublic void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. // 所有 BeanPostProcesser 初始化的调用点 registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. // 所有单例非懒加载 Bean 的调用点 finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); }}// AbstractAutowireCapableBeanFactory.classprotected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) { // 调用 Group1 中的 Aware invokeAwareMethods(beanName, bean); // 调用 Group2 中的 Aware // 实质上这里就是前面所说的 BeanPostProcessor 的调用点 // 与 Group1 不同,是通过 BeanPostProcessor(ApplicationContextAwareProcessor)实现的 Object wrappedBean = bean; wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); // 下文即将介绍的 InitializingBean 调用点 invokeInitMethods(beanName, wrappedBean, mbd); // BeanPostProcessor的另一个调用点 wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); return wrappedBean;}
public class TestService1 { public TestService2 testService2; public void test1() { }}public class TestService2 { private TestService1 testService1; public void test2() { }}
public class UserService { public void createUser(User user) { securityCheck(); Transaction tx = startTransaction(); try { // user相关业务 tx.commit(); } catch (RuntimeException e) { tx.rollback(); throw e; } log("created user: " + user); }}public class UserServiceProxy extends UserService { private UserService target; public User register(String email, String password) { // do something like securityCheck, transaction, log return target.register(email, password); }}public class InvocationHandlerImpl<T> implements InvocationHandler { private T target; public InvocationHandlerImpl(T target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // do something like securityCheck, transaction, log System.out.println("---------------"); Object result = method.invoke(target, args); System.out.println("---------------"); return result; } public static void main(String[] args) { // 创建被代理类 UserService target = new UserServiceImpl(); // 创建 InvocationHandler 动态代理类 InvocationHandlerImpl<UserService> invocationHandlerImpl = new InvocationHandlerImpl<UserService>(target); // 创建代理对象 UserService userService = (UserService) Proxy.newProxyInstance( UserService.class.getClassLoader(), new Class<?>[] {UserService.class}, invocationHandlerImpl); // 代理对象调用方法,进行增强 userService.getUserByUserNameAndPassword("hyman", "111111"); }}<!-- Spring AOP --><dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency><!-- 支持切入点表达式 --><dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>${aspectj.version}</version></dependency><!-- 支持 aop 注解 --><dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>${aspectj.version}</version></dependency>(RetentionPolicy.RUNTIME)(ElementType.METHOD)public @interface MethodInfoAnnotation {}public class MethodLoggerAspect { private final static Logger LOGGER = LogManager.getLogger(MethodLoggerAspect.class); private ObjectMapper mapper = new ObjectMapper(); /** * -第一个*代表返回类型不限 * -第二个*代表 module 包下所有子包 * -第三个*代表 server 包下所有类 * -第四个*代表所有方法 * (..) 代表参数不限 * Order 代表优先级,数字越小优先级越高 * -多个表达式之间使用 ||, or 表示或 ,使用 && , and 表示与 , !表示非 */ ("execution(public * com.sfac.springMvc.module.*.service.*.*(..)) || " + "@annotation(com.sfac.springMvc.aspect.MethodInfoAnnotation)")// @Pointcut("@annotation(com.sfac.springMvc.aspect.MethodInfoAnnotation)")// @Pointcut("execution(public * com.sfac.springMvc.module.*.service.*.*(..))") (1) public void methodLoggerPointCut() {} (value = "methodLoggerPointCut()") public void beforeAspect(JoinPoint joinPoint) throws JsonProcessingException { LOGGER.debug("======== Before Aspect ========"); String className = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); Object[] args = joinPoint.getArgs(); // 有些参数无法 Json 序列化 //List<String> argsList = new ArrayList<String>(); //for (Object item : args) { // argsList.add(mapper.writeValueAsString(item)); //} LOGGER.debug(String.format("Call class: %s", className)); LOGGER.debug(String.format("Call method: %s", methodName)); if (args.length > 0) { LOGGER.debug(String.format("Call method args: %s", args)); } } (value = "methodLoggerPointCut()") public Object aroundAspect(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { long startTime = System.currentTimeMillis(); Object result = proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs()); long endTime = System.currentTimeMillis(); LOGGER.debug(String.format("Elapse time: %s millisecond", (endTime - startTime))); return result; } (value = "methodLoggerPointCut()") public void afterAspect(JoinPoint joinPoint) { LOGGER.debug("======== After Aspect ========"); }}


<tx:annotation-driven /> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /></bean>


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.sfac</groupId> <artifactId>java_spring_mvc</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>java_spring_mvc Maven Webapp</name> <url>http://maven.apache.org</url> <properties> <java.version>1.8</java.version> <spring.version>5.2.0.RELEASE</spring.version> <aspectj.version>1.9.5</aspectj.version> <jackson.version>2.11.3</jackson.version> <tomcat.version>9.0.36</tomcat.version> </properties> <dependencies> <!-- Spring IOC --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <!-- Spring AOP --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <!-- 支持切入点表达式 --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>${aspectj.version}</version> </dependency> <!-- 支持 aop 注解 --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>${aspectj.version}</version> </dependency> <!-- Spring Web --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <!-- Spring Data Access --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${spring.version}</version> </dependency> <!-- Commons --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.10</version> </dependency> <!-- Jackson --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>${jackson.version}</version> </dependency> <!-- 序列化 LocalDateTime 等时间类 --> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>${jackson.version}</version> </dependency> <!-- Test --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> <build> <finalName>java_spring_mvc</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build></project> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <display-name>Java Spring Mvc Application</display-name> <!-- welcome page --> <welcome-file-list> <welcome-file>/index.jsp</welcome-file> </welcome-file-list> <!-- servlet 上下文 --> <!-- 指定 spring 配置文件,启动 spring 容器 --> <context-param> <!-- 启动容器就会加载此处配置的配置文件 第一种写法,配置文件放在 src/main/resources 下 第二种写法,配置文件放在 /WEB-INF/conf/spring 下 --> <param-name>contextConfigLocation</param-name> <param-value>classpath:config/applicationContext.xml</param-value> <!-- <param-value>/WEB-INF/config/applicationContext.xml</param-value> --> </context-param> <!-- listener --> <!-- spring 监听器:启动 Web 容器时,自动装配 ApplicationContext 的配置信息 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- filter --> <!-- 字符过滤器 --> <filter> <filter-name>characterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>characterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- servlet --> <servlet> <servlet-name>springMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:config/springMvcConfig.xml</param-value> </init-param> <!-- 值大于等于0表示容器启动应用时候加载该servlet,数值越小优先级越高 --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping></web-app> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:jaxws="http://cxf.apache.org/jaxws" 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-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <!-- 扫描构造型(stereotype)注解所标注的类,@Component、@Repository、@Service、@Controller 等 --> <context:component-scan base-package="com.sfac.springMvc"> <!-- 设置黑名单,不扫描 Controller --> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> </beans> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <!-- 扫描构造型(stereotype)注解所标注的类,@Component、@Repository、@Service、@Controller 等 --> <context:component-scan base-package="com.sfac.springMvc" use-default-filters="false"> <!-- 设置白名单,只扫描 Controller --> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <!-- 启用注解驱动:将扫描到的组件注册到工厂中,来处理请求; Spring 3.0.x 新增配置,其中最主要的两个类:DefaultAnnotationHandlerMapping 和 AnnotationMethodHandlerAdapter, 分别为 HandlerMapping 的实现类和 HandlerAdapter 的实现类, 从 3.1.x 版本开始对应实现类改为了 RequestMappingHandlerMapping 和 RequestMappingHandlerAdapter; RequestMappingHandlerMapping:处理 @RequestMapping 注解,并将其注册到请求映射表中; RequestMappingHandlerAdapter:处理请求的适配器,确定调用哪个类的哪个方法; --> <mvc:annotation-driven /> <!-- 处理静态资源方式一 Spring 3.0.x 新增 REST 风格,URL 不希望带 .html 或 .do 等后缀,在 web.xml 中配置 DispatcherServlet 的请求映射为 /,Spring MVC 将捕获 Web 容器所有的请求,包括静态资源的请求,但静态资源没有处理器,因此会报错; 该配置会在 Spring MVC 上下文中定义一个 DefaultServletHttpRequestHandler,它会像一个检查员, 对进入 DispatcherServlet 的 URL 进行筛查,如果是静态资源的请求,就将该请求转由 Web 服务器默认的 Servlet 处理, 如果不是静态资源的请求,才由 DispatcherServlet 继续处理; 一般 Web 服务器默认的 Servlet 名称是 "default",因此 DefaultServletHttpRequestHandler 可以找到它,如果你所有的 Web 服务器的默认 Servlet 名称不是 "default",则需要通过 default-servlet-name 属性显示指定; 该配置从项目 webapp 根目录开始计算路径,假设 webapp 下有 static 文件夹放置 css 和 js, 那么访问路径类似于:http://127.0.0.1/static/js/custom.js; --> <mvc:default-servlet-handler /> <!-- 处理静态资源方式二 该配置在处理静态资源上更进一步,由 Spring MVC 框架自己处理静态资源; location:没有前缀,从项目 webapp 根目录开始计算路径, 访问路径 = 映射路径 + 相对路径,类似于:http://127.0.0.1/static/js/custom.js; location: 添加 ‘file:’ 前缀,表示用本地文件夹作为静态资源文件夹, 访问路径 = 映射路径 + 相对路径,类似于:http://127.0.0.1/upload/test.jpg; --> <!-- <mvc:resources location="/static/" mapping="/**" /> --> <mvc:resources location="file:D:/upload/" mapping="/upload/**" /></beans>("/test")public class TestController { /** * 127.0.0.1/test/desc ---- get */ ("/desc") public String testDesc() { return "This is test module desc."; }}

<!-- Server runtime --><!-- Tomcat --><dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-core</artifactId> <version>${tomcat.version}</version> <scope>provided</scope></dependency><dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <version>${tomcat.version}</version> <scope>provided</scope></dependency>public class JavaSpringMvcApplication { private static int PORT = 80; private static String CONTEXT_PATH = "/"; public static void main(String[] args) throws LifecycleException {// String baseDir = Thread.currentThread().getContextClassLoader().getResource("").getPath(); String baseDir = new File("target/classes").getAbsolutePath(); String appDir = new File("src/main/webapp").getAbsolutePath(); System.out.println(baseDir); System.out.println(appDir); // 创建 Tomcat,并设置基础目录、端口、连接器 Tomcat tomcat = new Tomcat(); tomcat.setBaseDir(baseDir); tomcat.setPort(PORT); tomcat.getConnector(); // 添加 webapp Context context = tomcat.addWebapp(CONTEXT_PATH, appDir); WebResourceRoot resources = new StandardRoot(context); resources.addPreResources(new DirResourceSet(resources, "/WEB-INF/classes", baseDir, "/")); context.setResources(resources); // 启动服务器 tomcat.start(); tomcat.getServer().await(); }}<!-- Log4j --><dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>${log4j.version}</version></dependency><dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>${log4j.version}</version></dependency><dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-web</artifactId> <version>${log4j.version}</version></dependency> <Configuration status="OFF" monitorInterval="1800"> <!-- 配置常量 --> <properties> <property name="env">dev</property> <property name="project">java_spring_mvc</property> <property name="root">/log</property> <property name="fileSize">50MB</property> <property name="maxHistory">100</property> <property name="dateTime">yyyy-MM-dd HH:mm:ss</property> <property name="pattern">%d{${dateTime}} [%t] %-5level %logger{36} - %msg%n</property> </properties> <!-- 配置控制台输出和文件输出 --> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="${pattern}" /> </Console> <RollingFile name="running-log" fileName="${root}/${project}_${env}.log" filePattern="${root}/${project}_${env}_%d.%i.log" immediateFlush="true"> <PatternLayout pattern="${pattern}" /> <Policies> <TimeBasedTriggeringPolicy /> <SizeBasedTriggeringPolicy size="${fileSize}" /> </Policies> <DefaultRolloverStrategy max="${maxHistory}" /> </RollingFile> </Appenders> <Loggers> <!-- 指定第三方 jar 或自定义包的输出级别 --> <Logger name="org.springframework" level="info" additivity="true"> <AppenderRef ref="Console" /> <AppenderRef ref="running-log" /> </Logger> <!-- 指定根日志级别TRACE<DEBUG<INFO<WARN<ERROR --> <Root level="debug"> <AppenderRef ref="Console" /> <AppenderRef ref="running-log" /> </Root> </Loggers></Configuration><!-- 指定log4j配置文件 --><context-param> <param-name>log4jConfiguration</param-name> <param-value>classpath:config/log4j.xml</param-value></context-param><!-- Log4j监听器 --><listener> <listener-class>org.apache.logging.log4j.web.Log4jServletContextListener</listener-class></listener><!-- Log4j过滤器 --><filter> <filter-name>log4jServletFilter</filter-name> <filter-class>org.apache.logging.log4j.web.Log4jServletFilter</filter-class></filter><filter-mapping> <filter-name>log4jServletFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> <dispatcher>INCLUDE</dispatcher> <dispatcher>ERROR</dispatcher></filter-mapping>import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;/** * Description: Test Controller * @author HymanHu * @date 2020-12-08 10:10:23 */("/test")public class TestController { private final static Logger LOGGER = LogManager.getLogger(TestController.class); /** * 127.0.0.1/test/logger ---- get */ ("/logger") public String loggerTest() { LOGGER.trace("This is trace logger."); LOGGER.debug("This is debug logger."); LOGGER.info("This is info logger."); LOGGER.warn("This is warn logger."); LOGGER.error("This is error logger."); return "This is logger test."; }}
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String uri = request.getRequestURI(); if (uri.startsWith("/upload")) { request.getRequestDispatcher(URLDecoder.decode(uri, "utf-8")).forward(request, response); } else { filterChain.doFilter(request, response); }}spring.resource.path=/upload/spring.resource.path.pattern=${spring.resource.path}**spring.resource.folder.windows=d:/upload/springMvc/spring.resource.folder.linux=/upload/springMvc/<!-- 加载属性文件 --><bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:config/properties/springMvcParms.properties</value> </list> </property></bean><!-- 处理静态资源方式二 该配置在处理静态资源上更进一步,由 Spring MVC 框架自己处理静态资源; location:没有前缀,从项目 webapp 根目录开始计算路径, 访问路径 = 映射路径 + 相对路径,类似于:http://127.0.0.1/static/js/custom.js; location: 添加 ‘file:’ 前缀,表示用本地文件夹作为静态资源文件夹, 访问路径 = 映射路径 + 相对路径,类似于:http://127.0.0.1/upload/test.jpg;--><!-- <mvc:resources location="/static/" mapping="/**" /> --><!-- <mvc:resources location="file:d:/upload/springMvc/" mapping="/upload/**" /> --><mvc:resources location="file:${spring.resource.folder.windows}" mapping="${spring.resource.path.pattern}" /><!-- 加载属性文件 --><bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:config/properties/jdbcParms.properties</value> <value>classpath:config/properties/hibernateParms.properties</value> <value>classpath:config/properties/springMvcParms.properties</value> </list> </property></bean>("classpath:config/properties/springMvcParms.properties")public class ResourceConfigBean { ("${spring.resource.path}") private String resourcePath; ("${spring.resource.path.pattern}") private String resourcePathPattern; ("${spring.resource.folder.windows}") private String localPathForWindow; ("${spring.resource.folder.linux}") private String localPathForLinux; // 自行添加 get、set方法}private ResourceConfigBean resourceConfigBean;/** * 127.0.0.1/test/config ---- get */("/config")public ResourceConfigBean configTest() { return resourceConfigBean;}public class Result<T> { private int status; private String message; private T data; public Result() { } public Result(int status, String message) { this.status = status; this.message = message; } public Result(int status, String message, T data) { this.status = status; this.message = message; this.data = data; } // 自行添加 get、set方法 /** * Description: Result Status * @author HymanHu * @date 2020-12-09 16:06:12 */ public enum ResultStatus { SUCCESS(200), FAILED(500); public int code; private ResultStatus(int code) { this.code = code; } }}public class Search { private int currentPage; private int pageSize; private String sort; private String direction; private String keyword; public void init() { this.currentPage = this.currentPage == 0 ? 1 : this.currentPage; this.pageSize = this.pageSize == 0 ? 5 : this.pageSize; this.sort = StringUtils.isBlank(this.sort) ? "id" : this.sort; this.direction = StringUtils.isBlank(this.direction) ? "ASC" : this.direction; }}<dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId></dependency>| 技术 | 填充参数 | 映射结果集 | 查询语句 | 缓存 | 特点 |
| JdbcTemplate | 手动 | 手动 | 手动 | ||
| Mybatis | 自动 | 自动 | 手动 | 一二级缓存 | |
| Hibernate | 自动 | 自动 | 自动 | 一二级缓存 | Hql 转 Sql 多了性能开销; Sql 不够灵活,优化麻烦; 缓存增大了数据的不一致性; |
| Jpa | 自动 | 自动 | 自动 | 一二级缓存 | 引用是 javax.persistence 这个“标准”包,和 org.hibernate 实现包; |
<!-- Mysql --><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.48</version> <!-- <version>8.0.18</version> --></dependency><!-- 数据库连接池 --><dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.4</version></dependency><!-- Mybatis --><dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.1</version></dependency><dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.1</version></dependency><!-- Mybatis 处理 LocalDatetime --><dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-typehandlers-jsr310</artifactId> <version>1.0.2</version></dependency><dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>4.1.6</version></dependency><!-- mybatis generator --><dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.3.7</version></dependency><!-- 加载属性文件 --><bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:config/properties/jdbcParms.properties</value> </list> </property></bean><!-- 引入别的配置文件 --><import resource="springMybatis.xml"/>#jdbc.driverClassName=com.mysql.jdbc.Driver#jdbc.url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=falsejdbc.driverClassName=com.mysql.cj.jdbc.Driverjdbc.url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8jdbc.username=rootjdbc.password=root#连接池中连接用完时,c3p0一次性创建的连接数pool.acquireIncrement=5#初始化连接数,在minPoolSize和maxPoolSize之间pool.initialPoolSize=5pool.minPoolSize=5pool.maxPoolSize=200#连接关闭时默认将所有未提交的操作回滚,默认为falsepool.autoCommitOnClose=true <configuration> <settings> <!-- 打印 sql 语句,方式:SLF4J,LOG4J,LOG4J2,JDK_LOGGING,COMMONS_LOGGING,STDOUT_LOGGING,NO_LOGGING --> <setting name="logImpl" value="STDOUT_LOGGING" /> <!-- 开启驼峰映射 --> <setting name="mapUnderscoreToCamelCase" value="true" /> </settings> <plugins> <!-- 配置分页插件 --> <plugin interceptor="com.github.pagehelper.PageHelper"> <property name="dialect" value="mysql"/> </plugin> </plugins></configuration> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> <!-- 数据源,spring jdbc || c3p0 jdbc --> <!-- <bean id="dataSourceForMybatis" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> --> <!-- 配置 c3p0 数据源 --> <bean id="dataSourceForMybatis" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClassName}" /> <property name="jdbcUrl" value="${jdbc.url}" /> <property name="user" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <!-- 连接池中连接用完时,c3p0 一次性创建的连接数 --> <property name="acquireIncrement" value="${pool.acquireIncrement}" /> <!-- 初始化连接数,在 minPoolSize 和 maxPoolSize 之间 --> <property name="initialPoolSize" value="${pool.initialPoolSize}" /> <property name="minPoolSize" value="${pool.minPoolSize}" /> <property name="maxPoolSize" value="${pool.maxPoolSize}" /> <!-- 连接关闭时默认将所有未提交的操作回滚,默认为 false --> <property name="autoCommitOnClose" value="${pool.autoCommitOnClose}"/> </bean> <!-- sqlSessionFactory 单个数据库映射关系经过编译后的内存镜像 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 注入 dataSource --> <property name="dataSource" ref="dataSourceForMybatis"/> <!-- 扫描 Mybatis 配置文件 --> <property name="configLocation" value="classpath:config/mybatisConfig.xml"></property> <!-- 扫描 *Mapper.xml 文件 --> <property name="mapperLocations" value="classpath*:mybatis/*.xml" /> </bean> <!-- 配置一个可以执行批量的 sqlSessionTemplate --> <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/> <constructor-arg name="executorType" value="BATCH"/> </bean> <!-- 开启自动扫描 dao 接口 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> <property name="basePackage" value="com.sfac.springMvc.module.*.dao" /> </bean> <!-- 配置事务管理器 --> <bean id="mybatisTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSourceForMybatis"/> </bean> <!-- 开启事务注解 --> <tx:annotation-driven transaction-manager="mybatisTransactionManager" /></beans> <generatorConfiguration> <context id="DB2Tables" targetRuntime="MyBatis3"> <commentGenerator> <!-- 是否去除自动生成的注释 --> <property name="suppressAllComments" value="true"/> </commentGenerator> <!-- Mysql数据库连接的信息:驱动类、连接地址、用户名、密码 --> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false" userId="root" password="root"> </jdbcConnection> <!-- Oracle数据库 <jdbcConnection driverClass="oracle.jdbc.OracleDriver" connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:yycg" userId="yycg" password="yycg"> </jdbcConnection> --> <!-- 默认为false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true 时 把 JDBC DECIMAL 和 NUMERIC 类型解析为java.math.BigDecimal --> <javaTypeResolver > <property name="forceBigDecimals" value="false" /> </javaTypeResolver> <!-- targetProject:生成 POJO 类的位置 --> <javaModelGenerator targetPackage="com.sfac.springMvc.module.test.entity" targetProject="./src/main/java"> <!-- enableSubPackages: 是否让 schema 作为包的后缀 --> <property name="enableSubPackages" value="false" /> <!-- 从数据库返回的值被清理前后的空格 --> <property name="trimStrings" value="true" /> </javaModelGenerator> <!-- targetProject:mapper 映射文件生成的位置 --> <sqlMapGenerator targetPackage="mybatis" targetProject="./src/main/resources"> <!-- enableSubPackages:是否让 schema 作为包的后缀 --> <property name="enableSubPackages" value="false" /> </sqlMapGenerator> <!-- targetProject:mapper 接口生成的的位置 --> <javaClientGenerator type="XMLMAPPER" targetPackage="com.sfac.springMvc.module.test.dao" targetProject="./src/main/java"> <!-- enableSubPackages: 是否让 schema 作为包的后缀 --> <property name="enableSubPackages" value="false" /> </javaClientGenerator> <!-- 指定数据表 --> <table schema="" tableName="test_city"></table> <table schema="" tableName="test_country"></table> <!-- 有些表的字段需要指定java类型 <table schema="DB2ADMIN" tableName="ALLTYPES" domainObjectName="Customer" > <property name="useActualColumnNames" value="true"/> <generatedKey column="ID" sqlStatement="DB2" identity="true" /> <columnOverride column="DATE_FIELD" property="startDate" /> <ignoreColumn column="FRED" /> <columnOverride column="LONG_VARCHAR_FIELD" jdbcType="VARCHAR" /> </table> --> </context></generatorConfiguration>public class MybatisGeneratorSqlMap { public void generator() throws Exception { List<String> warnings = new ArrayList<String>(); boolean overwrite = true; // 指定配置文件 File configFile = new File( MybatisGeneratorSqlMap.class.getResource("/config/mybatisGeneratorConfig.xml").getPath()); ConfigurationParser cp = new ConfigurationParser(warnings); Configuration config = cp.parseConfiguration(configFile); DefaultShellCallback callback = new DefaultShellCallback(overwrite); MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings); myBatisGenerator.generate(null); } // 执行main方法以生成代码 public static void main(String[] args) { try { MybatisGeneratorSqlMap generatorSqlmap = new MybatisGeneratorSqlMap(); generatorSqlmap.generator(); } catch (Exception e) { e.printStackTrace(); } }}
public interface CityDao { // 要使用 useGeneratedKeys,要求参数必须包含生成的主键,否则抛出异常 // Error getting generated key or setting result to parameter object ("insert into test_city (city_id, city_name, local_city_name, country_id, district, population, " + "date_modified, date_created) values (#{cityId}, #{cityName}, #{localCityName}, " + "#{countryId}, #{district}, #{population}, #{dateModified}, #{dateCreated})") (useGeneratedKeys = true, keyColumn = "city_id", keyProperty = "cityId") void insertCity(City city);}public class CityServiceImpl implements CityService { private CityMapper cityMapper; private CityDao cityDao; public ResultEntity<City> insertCity(City city) { city.setDateCreated(new Date()); /* * -实现方式一:*Mapper.xml + *Mapper.java 接口 * -如果使用逆向工程生成的 Mapper.xml 文件,无法返回主键值,需要修改 Mapper.xml 中的 insert 方法; * <insert id="insert" parameterType="com.sfac.springMvc.entity.City" useGeneratedKeys="true" keyProperty="cityId"> */// cityMapper.insert(city); /* * -实现方式二:纯接口 + 注解开发 */ cityDao.insertCity(city); return new ResultEntity<City>(ResultStatus.SUCCESS.status, "Insert city success.", city); }}("/api")public class CityController { private CityService cityService; /** * 127.0.0.1/api/city ---- post * {"cityName":"dreamCity", "localCityName":"梦想城市", "countryId":522, "district":"sichuan", "population":1780000} */ (value = "/city", consumes = "application/json") public ResultEntity<City> insertCity( City city) { return cityService.insertCity(city); }}("update test_city set city_name = #{cityName}, local_city_name = #{localCityName}, " + "country_id = #{countryId}, district = #{district}, population = #{population}, " + "date_modified = #{dateModified} where city_id = #{cityId}")void updateCity(City city);public ResultEntity<City> updateCity(City city) { city.setDateModified(new Date()); cityDao.updateCity(city); int i = 1 / 0; return new ResultEntity<City>(ResultStatus.SUCCESS.status, "Update city success.", city);}/** * 127.0.0.1/api/city ---- put * cityId=2261,cityName=dreamCity,localCityName=梦想城市1, * countryId=522,district=sichuan,population=1780001 */(value = "/city", consumes = "application/x-www-form-urlencoded")public ResultEntity<City> updateCity( City city) { return cityService.updateCity(city);}<!-- 解决 PUT 请求无法提交表单数据的问题 --><filter> <filter-name>httpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class></filter><filter-mapping> <filter-name>httpMethodFilter</filter-name> <url-pattern>/*</url-pattern></filter-mapping><!-- 解决 Form 表单不支持 Put、Delete 请求问题,页面使用 _method 隐藏域指定真正的请求类型 --><filter> <filter-name>hiddenHttpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class></filter><filter-mapping> <filter-name>hiddenHttpMethodFilter</filter-name> <!-- 绑定 DispatcherServlet --> <servlet-name>springMVC</servlet-name></filter-mapping>("delete from test_city where city_id = #{cityId}")void deleteCityByCityId(int cityId);public ResultEntity<Object> deleteCityByCityId(int cityId) { cityDao.deleteCityByCityId(cityId); return new ResultEntity<Object>(ResultStatus.SUCCESS.status, "Delete city success.");}/** * 127.0.0.1/api/city/2260 ---- delete */("/city/{cityId}")public ResultEntity<Object> deleteCityByCityId( int cityId) { return cityService.deleteCityByCityId(cityId);}("select ci.*, co.country_name from test_city ci " + "left join country co on ci.country_id = co.country_id " + "where ci.city_id = #{cityId}")City getCityByCityId(int cityId);public City getCityByCityId(int cityId) { return cityDao.getCityByCityId(cityId);}/** * 127.0.0.1/api/city/2261 ---- get */("/city/{cityId}")public City getCityByCityId( int cityId) { return cityService.getCityByCityId(cityId);}("select * from test_city where country_id = #{countryId}")List<City> getCitiesByCountryId(int countryId);public interface CountryDao { /** * @Results ---- 封装结果集,对于联表查询的字段,可调用已有的方法 getCitiesByCountryId * column ---- 对应 select 查询后的某个字段名,作为映射实体 bean 属性或者作为调用方法的参数 * property ---- 对应 实体 bean 属性 * 1、country_id 封装了两次,分别对应 countryId 和 cities, * -而 cities 属性通过 getCitiesByCountryId 方法来实现,country_id作为参数 * 2、结果集共享,设置 id 属性,调用时使用 @ResultMap(value="countryResults") */ ("select * from test_country where country_id = #{countryId}") (id = "countryResults", value = { (column = "country_id", property = "countryId"), (column = "country_id", property = "cities", javaType = List.class, many = (select = "com.sfac.springMvc.dao.CityDao.getCitiesByCountryId") ) }) Country getCountryByCountryId(int countryId); ("select * from test_country where country_name = #{countryName}") (value = "countryResults") Country getCountryByCountryName(String countryName);}public class CountryServiceImpl implements CountryService { private CountryDao countryDao; public Country getCountryByCountryId(int countryId) { return countryDao.getCountryByCountryId(countryId); } public Country getCountryByCountryName(String countryName) { return countryDao.getCountryByCountryName(countryName); }}("/api")public class CountryController { private CountryService countryService; /** * 127.0.0.1/api/country/522 ---- get */ ("/country/{countryId}") public Country getCountryByCountryId( int countryId) { return countryService.getCountryByCountryId(countryId); } /** * 127.0.0.1/api/country?countryName=China ---- get */ ("/country") public Country getCountryByCountryName( String countryName) { return countryService.getCountryByCountryName(countryName); }}public class SearchBean { public final static int DEFAULT_CURRENT_PAGE = 1; public final static int DEFAULT_PAGE_SIZE = 5; private int currentPage; private int pageSize; private String keyWord; private String orderBy; private String direction; // 初始化 search bean public void initSearchBean() { if (this != null) { this.setCurrentPage(this.getCurrentPage() == 0 ? DEFAULT_CURRENT_PAGE : this.getCurrentPage()); this.setPageSize(this.getPageSize() == 0 ? DEFAULT_PAGE_SIZE : this.getPageSize()); this.setDirection(StringUtils.isBlank(this.getDirection()) ? "asc" : this.getDirection()); } } // 自行添加 get、set 方法}("<script>" + "select ci.*, co.country_name from test_city ci left join country co " + "on ci.country_id = co.country_id " + "<where> " + "<if test='keyWord != \"\" and keyWord != null'>" + " and (ci.city_name like '%${keyWord}%' " + "or ci.local_city_name like '%${keyWord}%' " + "or co.country_name like '%${keyWord}%') " + "</if>" + "</where>" + "<choose>" + "<when test='orderBy != \"\" and orderBy != null'>" + " order by ${orderBy} ${direction}" + "</when>" + "<otherwise>" + " order by city_id desc" + "</otherwise>" + "</choose>" + "</script>")List<City> getCitiesBySearchBean(SearchBean searchBean);("<script>" + "select * from common_video " + "<where> " + " and (grade <= #{grade} ) " + "<if test='type != \"\" and type != null'>" + " and (type = #{type}) " + "</if>" + "<if test='search.keyword != \"\" and search.keyword != null'>" + " and (name like '%${search.keyword}%' or subject like '%${search.keyword}%' or " + " number like '%${search.keyword}%' or star like '%${search.keyword}%' or imdb like '%${search.keyword}%') " + "</if>" + "</where>" + "<choose>" + "<when test='search.sort != \"\" and search.sort != null'>" + " order by ${search.sort} ${search.direction}" + "</when>" + "<otherwise>" + " order by id desc" + "</otherwise>" + "</choose>" + "</script>")List<Video> getVideosBySearchAndTypeAndGrade( ("search") Search search, ("grade") int grade, ("type") String type);public PageInfo<City> getCitiesBySearchBean(SearchBean searchBean) { searchBean.initSearchBean(); PageHelper.startPage(searchBean.getCurrentPage(), searchBean.getPageSize()); return new PageInfo<City>(Optional.ofNullable(cityDao.getCitiesBySearchBean(searchBean)) .orElse(Collections.emptyList()));}/** * 127.0.0.1/api/cities ---- post * {"currentPage":1, "pageSize":5, "keyWord":"china", "orderBy":"ci.city_id", "direction":"desc"} */(value = "/cities", consumes = "application/json")public PageInfo<City> getCitiesBySearchBean( SearchBean searchBean) { return cityService.getCitiesBySearchBean(searchBean);}("<script>" + "insert into test_city (city_id, city_name, local_city_name, country_id, district, " + "population, date_modified, date_created) values " + "<foreach collection='cities' item='city' index='index' separator=','>" + "(#{city.cityId}, #{city.cityName}, #{city.localCityName}, #{city.countryId}, " + "#{city.district}, #{city.population}, #{city.dateModified}, #{city.dateCreated})" + "</foreach>" + "</script>")(useGeneratedKeys = true, keyColumn = "city_id", keyProperty = "cityId")void batchInsertCities(("cities") List<City> cities);public ResultEntity<List<City>> batchInsertCities(List<City> cities) { cities.forEach(item -> { item.setDateCreated(new Date()); }); cityDao.batchInsertCities(cities); return new ResultEntity<List<City>>(ResultStatus.SUCCESS.status, "Insert city success.", cities);}/** * 127.0.0.1/api/cities/v2 ---- post * [{"cityName":"dreamCity1", "localCityName":"梦想城市1", "countryId":522, "district":"sichuan", * "population":1780000},{"cityName":"dreamCity2", "localCityName":"梦想城市2", "countryId":522, * "district":"sichuan", "population":1780000}] */(value = "/cities/v2", consumes = "application/json")public ResultEntity<List<City>> batchInsertCities( List<City> cities) { return cityService.batchInsertCities(cities);}<!-- Hibernate --><dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>5.4.25.Final</version></dependency><!-- 加载属性文件 --><bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:config/properties/jdbcParms.properties</value> <value>classpath:config/properties/hibernateParms.properties</value> </list> </property></bean><!-- 引入别的配置文件 --><import resource="springHibernate.xml"/>hibernate.hbm2ddl.auto=updatehibernate.implicit_naming_strategy=com.sfac.springMvc.config.HibernateNamingStrategyhibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialecthibernate.show_sql=truehibernate.format_sql=truehibernate.connection.autocommit=true<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> <!-- 数据源,spring jdbc || c3p0 jdbc --> <!-- <bean id="dataSourceForHibernate" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> --> <!-- 配置 c3p0 数据源 --> <bean id="dataSourceForHibernate" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClassName}" /> <property name="jdbcUrl" value="${jdbc.url}" /> <property name="user" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <!-- 连接池中连接用完时,c3p0 一次性创建的连接数 --> <property name="acquireIncrement" value="${pool.acquireIncrement}" /> <!-- 初始化连接数,在 minPoolSize 和 maxPoolSize 之间 --> <property name="initialPoolSize" value="${pool.initialPoolSize}" /> <property name="minPoolSize" value="${pool.minPoolSize}" /> <property name="maxPoolSize" value="${pool.maxPoolSize}" /> <!-- 连接关闭时默认将所有未提交的操作回滚,默认为 false --> <property name="autoCommitOnClose" value="${pool.autoCommitOnClose}"/> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSourceForHibernate" /> <!-- 扫描 Entity 包 --> <property name="packagesToScan"> <list> <value>com.sfac.springMvc.module.*.entity</value> </list> </property> <!-- 命名规则隐式策略 --> <property name="implicitNamingStrategy"> <!-- 采用自定义配置 --> <bean class="com.sfac.springMvc.config.HibernateNamingStrategy"></bean> </property> <property name="hibernateProperties"> <props> <!-- 动态生成表策略 --> <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop> <!-- 数据库方言 --> <prop key="hibernate.dialect">${hibernate.dialect}</prop> <prop key="hibernate.show_sql">${hibernate.show_sql}</prop> <prop key="hibernate.format_sql">${hibernate.format_sql}</prop> </props> </property> </bean> <!-- 引入hibernateTemplate --> <bean id="hibernateTemplate" class="org.springframework.orm.hibernate5.HibernateTemplate"> <property name="sessionFactory" ref="sessionFactory" /> <property name="checkWriteOperations" value="false"></property> </bean> <!-- 配置事务管理器 --> <bean id="hibernateTransactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <!-- 开启事务注解 --> <tx:annotation-driven transaction-manager="hibernateTransactionManager"/></beans>public class AbstractEntity { (strategy = GenerationType.IDENTITY) (nullable = false, updatable = false) private Integer id; (using = LocalDateTimeSerializer.class) (pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") (updatable = false) private LocalDateTime createDate; // 在 insert 之前,该方法被调用 public void preInsert() { setCreateDate(LocalDateTime.now()); }}(name = "test_student")public class Student extends AbstractEntity { private String studentName; /** * OneToOne:一对一关系中,有外键方使用 JoinColumn 注解,另一方使用 mappedBy 属性(可选) * targetEntity:目标实体 * cascade:联级操作 * fetch:加载数据策略 * JoinColumn * name 对应 test_student 表中 card_id 外键列 */ (targetEntity = Card.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER) (name = "card_id", unique = true) private Card studentCard; /** * ManyToMany,一方使用 JoinTable 注解,另一方配置 mappedBy 属性 * cascade:联级操作 * fetch:加载数据策略 * JsonIgnore:不序列化该字段,避免无限递归 */ (mappedBy = "students", cascade = CascadeType.ALL, fetch = FetchType.EAGER) private List<Clazz> clazzes;}(name = "test_card")public class Card extends AbstractEntity { (name = "card_no", length = 33, unique = true) private String cardNo; /** * OneToOne:一对一关系中,有外键方使用 JoinColumn 注解,另一方使用 mappedBy 属性(可选) * cascade:联级操作 * fetch:加载数据策略 * JsonIgnore:不序列化该字段,避免无限递归 */ (mappedBy = "studentCard", cascade = CascadeType.ALL, fetch = FetchType.EAGER) private Student student;}(name = "test_clazz")public class Clazz extends AbstractEntity { private String clazzName; /** * ManyToMany,一方使用 JoinTable 注解,另一方配置 mappedBy 属性 * cascade:联级操作 * fetch:加载数据策略 * JoinTable:中间表 * name: 中间表表名 * joinColumns:表 test_clazz 参与中间表的主键 * inverseJoinColumns:关联表 test_student 参与中间表的主键 */ (cascade = CascadeType.ALL, fetch = FetchType.EAGER) (name = "test_clazz_student", joinColumns = (name = "clazz_id"), inverseJoinColumns = (name="student_id")) private List<Student> students; /** * ManyToOne:多方使用 joinClumn,创建外键,一方使用 mappedBy 属性 * cascade:联级操作 * fetch:加载数据策略 * JoinColumn * name:多方 test_clazz 表的外键 school_id * insertable & updatable:标识该属性是否参与插入和更新插入 * JsonIgnore:不序列化该字段,避免无限递归 */ (cascade = CascadeType.ALL, fetch = FetchType.EAGER) (name = "school_id", insertable = false, updatable = false) private School school;}(name = "test_school")public class School extends AbstractEntity { private String schoolName; // 定义数据库 text 类型,存储 64 KB 数据 (columnDefinition="text") private String description; private String region; /** * OneToMany:多方使用 joinClumn,创建外键,一方使用 mappedBy 属性 * cascade:联级操作 * fetch:加载数据策略 */ (mappedBy = "school", cascade = CascadeType.ALL, fetch = FetchType.EAGER) private List<Clazz> clazzes;}public class StudentServiceImpl implements StudentService { private HibernateTemplate hibernateTemplate; ("hibernateTransactionManager") public ResultEntity<Student> insertStudentForHibernate(Student student) { hibernateTemplate.saveOrUpdate(student); return new ResultEntity<Student>(ResultStatus.SUCCESS.status, "Insert success.", student); }}("/api")public class StudentController { private StudentService studentService; /** * 127.0.0.1/api/hibernate/student ---- post * {"studentName":"HymanHu"} * {"studentName":"HymanHu", "studentCard":{"cardNo":"studentCard001"}} */ (value = "/hibernate/student", consumes = "application/json") public ResultEntity<Student> insertStudentForHibernate( Student student) { return studentService.insertStudentForHibernate(student); }}// 系统中配置又多个事务管理器,所以需要在调用的时候需要指明("hibernateTransactionManager")public ResultEntity<Student> updateStudentForHibernate(Student student) { hibernateTemplate.saveOrUpdate(student);// int i = 1 / 0; return new ResultEntity<Student>(ResultStatus.SUCCESS.status, "Update success.", student);}/** * 127.0.0.1/api/hibernate/student ---- put * {"id":"2","studentName":"HymanHu1"} * {"id":"2","studentName":"HymanHu1","studentCard":{"id":"1","cardNo":"studentCard002"}} */(value = "/hibernate/student", consumes = "application/json")public ResultEntity<Student> updateStudentForHibernate( Student student) { return studentService.updateStudentForHibernate(student);}("hibernateTransactionManager")public ResultEntity<Object> deleteStudentForHibernate(Integer id) { Student student = new Student(); student.setId(id); hibernateTemplate.delete(student); return new ResultEntity<Object>(ResultStatus.SUCCESS.status, "Delete success.");}/** * 127.0.0.1/api/hibernate/student/2 ---- delete */("/hibernate/student/{id}")public ResultEntity<Object> deleteStudentForHibernate( Integer id) { return studentService.deleteStudentForHibernate(id);}public Student getStudentByIdForHibernate(Integer id) { return hibernateTemplate.get(Student.class, id);}({ "unchecked", "deprecation" })public Student getStudentByNameForHibernate(String studentName) { // Example 查询 Student example = new Student(); example.setStudentName(studentName); List<Student> students = hibernateTemplate.findByExample(example); // Hql 查询 students = (List<Student>) hibernateTemplate.find("from Student where studentName like ?0", String.format("%%%s%%", studentName)); return students.isEmpty() ? null : students.get(0);}/** * criteria 多条件查询,示例 sql * select * from test_student where ( studentName like ? and ( id between ? and ? or this_.card_id is null)) */("unchecked")public List<Student> getStudentsByNameForHibernate(String studentName) { DetachedCriteria criteria = DetachedCriteria.forClass(Student.class); criteria.add( Restrictions.and( Restrictions.like("studentName", studentName, MatchMode.ANYWHERE), Restrictions.or( Restrictions.between("id", 1, 10), Restrictions.isNull("studentCard") ) ) ); return (List<Student>) hibernateTemplate.findByCriteria(criteria);}/** * 127.0.0.1/api/hibernate/student/9 ---- get */("/hibernate/student/{id}")public Student getStudentByIdForHibernate( Integer id) { return studentService.getStudentByIdForHibernate(id);}/** * 127.0.0.1/api/hibernate/student?studentName=HymanHu ---- get */("/hibernate/student")public Student getStudentByNameForHibernate( String studentName) { return studentService.getStudentByNameForHibernate(studentName);}/** * 127.0.0.1/api/hibernate/students?studentName=HymanHu ---- get */("/hibernate/students")public List<Student> getStudentsForHibernate((required = false) String studentName) { return studentService.getStudentsByNameForHibernate(studentName);}
<!-- Spring data jpa --><dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>2.3.4.RELEASE</version></dependency><!-- 加载属性文件 --><bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:config/properties/jdbcParms.properties</value> <value>classpath:config/properties/hibernateParms.properties</value> </list> </property></bean><!-- 引入别的配置文件 --><import resource="springJpa.xml"/><?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd" > <!-- 数据源,spring jdbc || c3p0 jdbc --> <!-- <bean id="dataSourceForJpa" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> --> <!-- 配置 c3p0 数据源 --> <bean id="dataSourceForJpa" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClassName}" /> <property name="jdbcUrl" value="${jdbc.url}" /> <property name="user" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <!-- 连接池中连接用完时,c3p0 一次性创建的连接数 --> <property name="acquireIncrement" value="${pool.acquireIncrement}" /> <!-- 初始化连接数,在 minPoolSize 和 maxPoolSize 之间 --> <property name="initialPoolSize" value="${pool.initialPoolSize}" /> <property name="minPoolSize" value="${pool.minPoolSize}" /> <property name="maxPoolSize" value="${pool.maxPoolSize}" /> <!-- 连接关闭时默认将所有未提交的操作回滚,默认为 false --> <property name="autoCommitOnClose" value="${pool.autoCommitOnClose}"/> </bean> <!-- 配置 entityManagerFactory --> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <!-- 设置数据源 --> <property name="dataSource" ref="dataSourceForJpa" /> <!-- 扫描 Entity 包 --> <property name="packagesToScan" value="com.sfac.springMvc.module.*.entity" /> <!-- 指定 JPA 实现厂商 --> <property name="persistenceProvider"> <bean class="org.hibernate.jpa.HibernatePersistenceProvider" /> </property> <!-- 指定 JPA 供应商适配器 --> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" /> </property> <!-- 配置 JPA 属性 --> <property name="jpaProperties"> <props> <!-- 动态生成表策略 --> <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop> <!-- 命名规则隐式策略 --> <prop key="hibernate.implicit_naming_strategy">${hibernate.implicit_naming_strategy}</prop> <!-- 数据库方言 --> <prop key="hibernate.dialect">${hibernate.dialect}</prop> <prop key="hibernate.show_sql">${hibernate.show_sql}</prop> <prop key="hibernate.format_sql">${hibernate.format_sql}</prop> </props> </property> </bean> <!-- 配置事务管理器 --> <bean id="jpaTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <!-- 开启事务注解 --> <tx:annotation-driven transaction-manager="jpaTransactionManager"/> <!-- 配置 repository --> <jpa:repositories base-package="com.sfac.springMvc.module.*.repository" transaction-manager-ref="jpaTransactionManager" entity-manager-factory-ref="entityManagerFactory" /></beans>
public interface StudentRepository extends JpaRepository<Student, Integer> {}private StudentRepository studentRepository;("jpaTransactionManager")public ResultEntity<Student> insertStudentForJpa(Student student) { studentRepository.saveAndFlush(student); return new ResultEntity<Student>(ResultStatus.SUCCESS.status, "Insert success.", student);}/** * 127.0.0.1/api/jpa/student ---- post * {"studentName":"HymanHu"} * {"studentName":"HymanHu", "studentCard":{"cardNo":"studentCard001"}} */(value = "/jpa/student", consumes = "application/json")public ResultEntity<Student> insertStudentForJpa( Student student) { return studentService.insertStudentForJpa(student);}("jpaTransactionManager")public ResultEntity<Student> updateStudentForJpa(Student student) { studentRepository.saveAndFlush(student);// int i = 1 / 0; return new ResultEntity<Student>(ResultStatus.SUCCESS.status, "Update success.", student);}/** * 127.0.0.1/api/jpa/student ---- put * {"id":"2","studentName":"HymanHu1"} * {"id":"2","studentName":"HymanHu1","studentCard":{"id":"1","cardNo":"studentCard002"}} */(value = "/jpa/student", consumes = "application/json")public ResultEntity<Student> updateStudentForJpa( Student student) { return studentService.updateStudentForJpa(student);}public ResultEntity<Object> deleteStudentForJpa(Integer id) { studentRepository.deleteById(id); return new ResultEntity<Object>(ResultStatus.SUCCESS.status, "Delete success.");}/** * 127.0.0.1/api/jpa/student/2 ---- delete */("/jpa/student/{id}")public ResultEntity<Object> deleteStudentForJpa( Integer id) { return studentService.deleteStudentForJpa(id);}public Student getStudentByIdForJpa(Integer id) { return studentRepository.findById(id).orElse(null);}public List<Student> getStudentsForJpa() { return studentRepository.findAll();}/** * 127.0.0.1/api/jpa/student/9 ---- get */("/jpa/student/{id}")public Student getStudentByIdForJpa( Integer id) { return studentService.getStudentByIdForJpa(id);}/** * 127.0.0.1/api/jpa/students ---- get */("/jpa/students")public List<Student> getStudentsForJpa((required = false) String studentName) { return studentService.getStudentsForJpa();}public interface StudentRepository extends JpaRepository<Student, Integer>, JpaSpecificationExecutor<Student> {}public Page<Student> getStudentsBySearchBeanForJpa(SearchBean searchBean) { searchBean.initSearchBean();// Page<Student> page = exampleAndPage(searchBean); Page<Student> page = criteriaAndPage(searchBean); return page;}/** * -实现方式一:Example + Page 查询 */public Page<Student> exampleAndPage(SearchBean searchBean) { // 创建 Pageable 对象 String orderBy = StringUtils.isBlank(searchBean.getOrderBy()) ? "id" : searchBean.getOrderBy(); Sort.Direction direction = StringUtils.isBlank(searchBean.getDirection()) || searchBean.getDirection().equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; Sort sort = new Sort(direction, orderBy); // 当前页起始为 0 Pageable pageable = PageRequest.of(searchBean.getCurrentPage() - 1, searchBean.getPageSize(), sort); // 创建 Example 对象 Student student = new Student(); student.setStudentName(searchBean.getKeyWord()); student.setCreateDate(LocalDateTime.of(2021, 1, 19, 0, 0)); // matchingAny 相当于 or 连接查询条件,matching 相当于 and 连接查询条件 ExampleMatcher exampleMatcher = ExampleMatcher.matchingAny() // 模糊查询,即 %{studentName} % .withMatcher("studentName", match -> match.contains()) // 时间类型不支持模糊查询,生成的语句为createDate=?,同时也不支持 id > startId && id < endId 这样的操作 .withMatcher("createDate", match -> match.contains()) // 忽略基本数据类型字段,如果使用包装类则无需忽略 .withIgnorePaths("id"); Example<Student> example = Example.of(student, exampleMatcher); return studentRepository.findAll(example, pageable);}/** * -实现方式:Criteria + Page */public Page<Student> criteriaAndPage(SearchBean searchBean) { // 创建 Pageable 对象 String orderBy = StringUtils.isBlank(searchBean.getOrderBy()) ? "id" : searchBean.getOrderBy(); Sort.Direction direction = StringUtils.isBlank(searchBean.getDirection()) || searchBean.getDirection().equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; Sort sort = new Sort(direction, orderBy); // 当前页起始为 0 Pageable pageable = PageRequest.of(searchBean.getCurrentPage() - 1, searchBean.getPageSize(), sort); // 创建 Specification 对象 Specification<Student> specification = new Specification<Student>() { private static final long serialVersionUID = 1L; /** * -构造语句 select * from test_student where createDate>=? and * (studentName like ? or id between 10 and 20) order by id desc limit ? */ public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) { return criteriaBuilder.and( criteriaBuilder.greaterThanOrEqualTo( root.get("createDate").as(LocalDateTime.class), LocalDateTime.of(2021, 1, 20, 0, 0)), criteriaBuilder.or( criteriaBuilder.like(root.get("studentName").as(String.class), String.format("%%%s%%", searchBean.getKeyWord())), criteriaBuilder.between(root.get("id"), 10, 20) ) ); } }; return studentRepository.findAll(specification, pageable);}/** * 127.0.0.1/api/jpa/students ---- post * {"currentPage":1, "pageSize":5, "keyWord":"hy", "orderBy":"id", "direction":"desc"} * {"currentPage":1, "pageSize":5, "keyWord":"hu", "orderBy":"studentName", "direction":"asc"} * -注意:排序字段不能使用下划线 * */(value = "/jpa/students", consumes = "application/json")public Page<Student> getStudentsBySearchBeanForJpa( SearchBean searchBean) { return studentService.getStudentsBySearchBeanForJpa(searchBean);}List<Student> findByStudentName(String studentName);public Student getStudentByNameForJpa(String studentName) { List<Student> students = Optional.ofNullable(studentRepository.findByStudentName(studentName)) .orElse(Collections.emptyList()); return students.isEmpty() ? null : students.get(0);}/** * 127.0.0.1/api/jpa/student?studentName=HymanHu ---- get */("/jpa/student")public Student getStudentByNameForJpa( String studentName) { return studentService.getStudentByNameForJpa(studentName);}//@Query(nativeQuery = true, value = "select * from test_student where id = :id")(value = "from Student where id = :id")Student getStudentById(("id") Integer id);//@Query(nativeQuery = true, value = "update test_student set student_name = :studentName where id = :id")(value = "update Student set studentName = :studentName where id = :id")void updateStudentName(("studentName") String studentName, ("id") Integer id);(nativeQuery = true, value = "update test_student " + "set student_name = :#{#student.studentName} where id = :#{#student.id}")void updateStudent(("student") Student student);public Student getStudentByIdV2ForJpa(Integer id) { return studentRepository.getStudentById(id);}("jpaTransactionManager")public ResultEntity<Student> updateStudentNameForJpa(Student student) { studentRepository.updateStudentName(student.getStudentName(), student.getId()); return new ResultEntity<Student>(ResultStatus.SUCCESS.status, "Update success.", student);}/** * 127.0.0.1/api/jpa/student/2/v2 ---- get */("/jpa/student/{id}/v2")public Student getStudentByIdV2ForJpa( Integer id) { return studentService.getStudentByIdV2ForJpa(id);}/** * 127.0.0.1/api/jpa/student/v2 ---- put * {"id":"1","studentName":"HymanHu1"} */(value = "/jpa/student/v2",consumes = "application/json")public ResultEntity<Student> updateStudentNameForJpa( Student student) { return studentService.updateStudentNameForJpa(student);}private EntityManager entityManager;public ResultEntity<List<Student>> batchInsertStudentsForJpa(List<Student> students) { // 方式一,调用父接口完成批量操作// studentRepository.saveAll(students);// studentRepository.flush(); // 方式二,引入 entityManager 进行批量操作,效率高于第一种方式 students.stream().forEach(item -> { entityManager.persist(item); }); entityManager.flush(); entityManager.clear(); return new ResultEntity<List<Student>>(ResultStatus.SUCCESS.status, "Update success.", students);}/** * 127.0.0.1/api/jpa/students/v2 ---- post * [{"studentName":"aa1"},{"studentName":"aa2"}] */(value = "/jpa/students/v2", consumes = "application/json")public ResultEntity<List<Student>> batchInsertStudentsForJpa( List<Student> students) { return studentService.batchInsertStudentsForJpa(students);}<!-- Jsp 支持 --><dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope></dependency><dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.3</version> <scope>provided</scope></dependency><dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version></dependency><dependency> <groupId>org.apache.taglibs</groupId> <artifactId>taglibs-standard-impl</artifactId> <version>1.2.5</version></dependency>
<!-- 视图解析器 --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/templates/" p:suffix=".jsp"></bean>


<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %><%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %><html lang="en"><head> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <meta charset="utf-8" /> <meta name="description" content="overview & stats" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" /> <title>Java_Spring_Mvc</title> <!-- css --> <!-- bootstrap --> <link href="/static/css/bootstrap.min.css" rel="stylesheet" /> <!-- fontawesome --> <link href="/static/font-awesome/4.5.0/css/font-awesome.min.css" rel="stylesheet" /> <!-- text fonts --> <link href="/static/css/fonts.googleapis.com.css" rel="stylesheet" /> <!-- ace styles --> <link href="/static/css/ace.min.css" class="ace-main-stylesheet" id="main-ace-style" rel="stylesheet" /> <link href="/static/css/ace-skins.min.css" rel="stylesheet" /> <link href="/static/css/ace-rtl.min.css" rel="stylesheet" /></head><body class="no-skin"> <!-- header --> <%@ include file="../fragments/header.jsp"%> <div class="main-container ace-save-state" id="main-container"> <!-- sidebar --> <%@ include file="../fragments/sidebar.jsp"%> <!-- main content --> <div class="main-content"> <div class="main-content-inner"> <div class="breadcrumbs ace-save-state" id="breadcrumbs"> <ul class="breadcrumb"> <li><i class="ace-icon fa fa-home home-icon"></i><a href="#">Home</a></li> <li class="active">Test</li> </ul> <div class="nav-search" id="nav-search"> <form class="form-search"> <span class="input-icon"> <input type="text" placeholder="Search ..." class="nav-search-input" id="nav-search-input" autocomplete="off" /> <i class="ace-icon fa fa-search nav-search-icon"></i> </span> </form> </div> </div> <div class="page-content"> <div class="page-header"> <h1> Test <small> <i class="ace-icon fa fa-angle-double-right"></i> index page </small> </h1> </div> <!-- /.page-header --> <div class="row"> <div class="col-xs-12"> <div class="row"> <div class="space-6"></div> <h1>This is jsp test index page.</h1> <h3>获取变量</h3> <p>userName: ${userName }</p> <p>==================================================================</p> <h3>数据格式化</h3> <p>Date格式化: <fmt:formatDate value="${current1}" pattern="yyyy-MM-dd HH:mm:ss"/></p> <p>LocalDateTime格式化: <fmt:parseDate value="${current2}" pattern="y-M-dd'T'H:m:s" var="myParseDate"></fmt:parseDate> <fmt:formatDate value="${myParseDate}" pattern="yyyy-MM-dd HH:mm:ss"></fmt:formatDate > </p> <p>数字式化百分比: <fmt:formatNumber type="percent" value="${number / 100 }" maxFractionDigits="3" /> <p>数字式化货币: <fmt:formatNumber type="currency" value="${number }" /> <p>数字式化: <fmt:formatNumber type="number" value="${number }" maxFractionDigits="2" /> <p>==================================================================</p> <h3>判断标签</h3> <c:set var="title" scope="page" value="青年"/> <c:if test="${age > 10 && age < 20}"> <p>称谓: <c:out value="${title}"/><p> </c:if> <c:choose> <c:when test="${age < 10}"> <p>称谓: 儿童<p> </c:when> <c:when test="${age >= 10 && age < 30}"> <p>称谓: 青年<p> </c:when> <c:when test="${age >= 30 && age < 50}"> <p>称谓: 中年<p> </c:when> <c:otherwise> <p>称谓: 老年<p> </c:otherwise> </c:choose> <p>==================================================================</p> <h3>循环标签</h3> <ul> <c:forEach var="city" items="${cities }"> <li>${city.cityName }</li> </c:forEach> </ul> <table> <thead><tr><td>Id</td><td>CityName</td></tr></thead> <tbody> <c:forEach var="city" items="${cities }"> <tr><td>${city.cityId }</td><td>${city.cityName }</td></tr> </c:forEach> </tbody> </table> <select> <c:forEach var="city" items="${cities }"> <option id="${city.cityId }">${city.cityName }</option> </c:forEach> </select> <p>==================================================================</p> <h3>Form</h3> <form action="/test/city" method="post"> <input type="hidden" name="_method" value="put"> <input type="hidden" value="${city.cityId }" name="cityId" /> <p>City Name: <input type="text" name="cityName" value="${city.cityName }" /></p> <p>Local City Name: <input type="text" name="localCityName" value="${city.localCityName }"/></p> <p><button type="submit">Submit</button></p> </form> <p>==================================================================</p> </div> </div> </div> </div> <!-- /.page-content --> </div> </div> <!-- /.main-content --> <!-- footer --> <%@ include file="../fragments/footer.jsp"%> </div> <!-- /.main-container --> <!-- js --> <script src="/static/js/jquery-2.1.4.min.js"></script> <script src="/static/js/bootstrap.min.js"></script> <script src="/static/js/ace-elements.min.js"></script> <script src="/static/js/ace.min.js"></script> <script src="/static/js/ace-extra.min.js"></script> <script src="/static/js/custom.js"></script></body></html>/** * 127.0.0.1/test/index ---- get */("/index")public String testIndexPage(ModelMap modelMap) { List<City> cities = cityService.getCitiesByCountryId(522); City city = cityService.getCityByCityId(2255); modelMap.put("userName", "HymanHu"); modelMap.put("current1", new Date()); modelMap.put("current2", LocalDateTime.now()); modelMap.put("number", 23.453); modelMap.put("age", 18); modelMap.put("cities", cities.stream().limit(10).collect(Collectors.toList())); modelMap.put("city", city); return "test/index";}/** * 127.0.0.1/test/city ---- put */(value = "/city", consumes = "application/x-www-form-urlencoded")public String updateCityForm( City city) { cityService.updateCity(city); return "redirect:/test/index";}public class OnlineUserCounterListener implements HttpSessionListener, HttpSessionAttributeListener, ServletRequestListener { private final static Logger LOGGER = LogManager.getLogger(OnlineUserCounterListener.class); private static ThreadLocal<HttpServletRequest> httpServletRequestHolder = new ThreadLocal<HttpServletRequest>(); // Session 创建时,输出 Session id public void sessionCreated(HttpSessionEvent se) { LOGGER.debug(String.format("New session id: %s", se.getSession().getId())); } // Session 销毁时候,计数器减一 public void sessionDestroyed(HttpSessionEvent se) { ServletContext servletContext = se.getSession().getServletContext(); Integer count = servletContext.getAttribute("onlineUserCounter") == null ? 0 : (Integer) servletContext.getAttribute("onlineUserCounter"); count --; servletContext.setAttribute("onlineUserCounter", count); } // Session 添加 user,计数器加一,并输出用户登录信息 public void attributeAdded(HttpSessionBindingEvent se) { ServletContext servletContext = se.getSession().getServletContext(); Integer count = servletContext.getAttribute("onlineUserCounter") == null ? 0 : (Integer) servletContext.getAttribute("onlineUserCounter"); if ("user".equals(se.getName())) { count ++; servletContext.setAttribute("onlineUserCounter", count); User user = (User) se.getSession().getAttribute("user"); /* * -因为 Listener 是由 Servlet 负责实例化,如果直接注入 RoleService, * -拿不到 Spring 容器的 Bean,因此使用 WebApplicationContextUtils */ RoleService roleService = WebApplicationContextUtils.getWebApplicationContext(servletContext).getBean(RoleService.class); List<Role> roles = roleService.getRolesByUserId(user.getId()); HttpServletRequest httpServletRequest = httpServletRequestHolder.get(); LOGGER.debug(String.format("Online user count: %s", count)); LOGGER.debug(String.format("Login user name: %s", user.getUserName())); LOGGER.debug(String.format("Login user roles: %s", roles.stream().map(item -> item.getRoleName()).collect(Collectors.toList()))); LOGGER.debug(String.format("Login user ip: %s", httpServletRequest.getRemoteAddr())); LOGGER.debug(String.format("Login date time: %s", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()))); } } // Session 移除 user,计数器减一 public void attributeRemoved(HttpSessionBindingEvent se) { ServletContext servletContext = se.getSession().getServletContext(); Integer count = servletContext.getAttribute("onlineUserCounter") == null ? 0 : (Integer) servletContext.getAttribute("onlineUserCounter"); if ("user".equals(se.getName())) { count --; servletContext.setAttribute("onlineUserCounter", count); } } public void attributeReplaced(HttpSessionBindingEvent se) { } public void requestInitialized(ServletRequestEvent sre) { httpServletRequestHolder.set((HttpServletRequest)sre.getServletRequest()); } public void requestDestroyed(ServletRequestEvent sre) { httpServletRequestHolder.remove(); }}
public class AuthFilter implements Filter { private final static Logger LOGGER = LogManager.getLogger(AuthFilter.class); private UserService userService; /** * Authorization: Basic userName:password */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest)request; HttpServletResponse resp = (HttpServletResponse) response; LOGGER.debug("RequestUri: " + req.getRequestURI()); String authHeader = req.getHeader("Authorization"); if (StringUtils.isNotBlank(authHeader) && authHeader.startsWith("Basic ")) { String userNameAndPassword = authHeader.substring(authHeader.indexOf(" ") + 1); String userName = userNameAndPassword.split(":")[0]; String password = userNameAndPassword.split(":")[1]; LOGGER.debug(String.format("Request header Auth:%s - %s", userName, password)); User user = userService.getUserByUserNameAndPassword(userName, password); if (user != null) { LOGGER.debug(String.format("User %s is auth success.", user.getUserName())); req.getSession().setAttribute("user", user); } } chain.doFilter(req, resp); }}<filter> <filter-name>authFilter</filter-name> <filter-class>com.sfac.springMvc.filter.AuthFilter</filter-class></filter><filter-mapping> <filter-name>authFilter</filter-name> <url-pattern>/*</url-pattern></filter-mapping><filter> <filter-name>authFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class></filter><filter-mapping> <filter-name>authFilter</filter-name> <url-pattern>/*</url-pattern></filter-mapping>
public class ControllerMonitorInterceptor extends HandlerInterceptorAdapter { private final static Logger LOGGER = LogManager.getLogger(AuthFilter.class); private static ThreadLocal<Long> threadLocal = new ThreadLocal<Long>(); public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { LOGGER.debug("======== Pre Interceptor ========"); long startTime = System.currentTimeMillis(); threadLocal.set(startTime); return true; } public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { LOGGER.debug(String.format("Request uri: %s", request.getRequestURI())); LOGGER.debug(String.format("Request method: %s", request.getMethod())); LOGGER.debug(String.format("Request remote address: %s", request.getRemoteAddr())); } public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { long endTime = System.currentTimeMillis(); long startTime = threadLocal.get(); LOGGER.debug(String.format("Elapse time: %s millisecond", (endTime - startTime))); LOGGER.debug("======== After Interceptor ========"); }}<!-- 注册拦截器,可注册多个 --><mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**" /> <bean id="controllerMonitorInterceptor" class="com.sfac.springMvc.interceptor.ControllerMonitorInterceptor"></bean> </mvc:interceptor></mvc:interceptors>
(name = "common_exception_log")public class ExceptionLog extends AbstractEntity { private String ip; private String path; private String className; private String methodName; private String exceptionType; private String exceptionMessage;}public class SpringMvcHandlerExceptionResolver implements HandlerExceptionResolver { private final static Logger LOGGER = LogManager.getLogger(SpringMvcHandlerExceptionResolver.class); private ExceptionLogService exceptionLogService; public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { ex.printStackTrace(); if (handler instanceof HandlerMethod) { LOGGER.debug("======== Log exception into db ========"); String ip = IPUtils.getIpAddr(request); HandlerMethod handlerMethod = (HandlerMethod) handler; String className = handlerMethod.getBeanType().getName(); String methodName = handlerMethod.getMethod().getName(); String exceptionType = ex.getClass().getSimpleName(); String exceptionMessage = ex.getMessage(); ExceptionLog exceptionLog = new ExceptionLog(); exceptionLog.setIp(ip); exceptionLog.setPath(request.getServletPath()); exceptionLog.setClassName(className); exceptionLog.setMethodName(methodName); exceptionLog.setExceptionType(exceptionType); exceptionLog.setExceptionMessage(exceptionMessage); exceptionLog.setCreateDate(LocalDateTime.now()); exceptionLogService.insertExceptionLog(exceptionLog); if (isInterface(handlerMethod)) { return jsonResult(ResultEntity.ResultStatus.FAILED.status, exceptionMessage); } else { return pageResult("common/500", exceptionMessage); } } return null; } // 判断目标方法是否为接口 private boolean isInterface(HandlerMethod handlerMethod) { Annotation[] classAnnotations = handlerMethod.getBeanType().getAnnotationsByType(RestController.class); Annotation[] methodAnnotations = handlerMethod.getMethod().getAnnotationsByType(ResponseBody.class); return (classAnnotations.length > 0) || (methodAnnotations.length > 0); } // 判断是否是 Ajax 请求 private boolean isAjax(HttpServletRequest request) { return "XMLHttpRequest".equalsIgnoreCase(request.getHeader("X-Requested-With")); } // 返回页面 private ModelAndView pageResult(String url, String message) { Map<String, String> model = new HashMap<String, String>(); model.put("message", message); // 直接返回页面 //return new ModelAndView(url, model); // 重定向到错误页面控制器 return new ModelAndView("redirect:/common/500", model); } // 返回 Json 数据 private ModelAndView jsonResult(int status, String message) { ModelAndView mv = new ModelAndView(new MappingJackson2JsonView()); mv.addObject("status", status); mv.addObject("message", message); return mv; }}<!-- Spring Mvc 文件上传 --><dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version></dependency><!-- 上传文件 --><bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 1TB=1024GB,1GB=1024MB,1MB=1024KB,1KB=1024Byte,1Byte=8bit --> <!-- 此处配置数值的单位是Byte,设置上传文件的最大尺寸为 10M:10*1024*1024 --> <property name="maxUploadSize" value="10485760"></property> <property name="defaultEncoding" value="UTF-8"></property></bean><p>==================================================================</p><h3>File Upload</h3><p>${message}</p><img src="${relativePath }" width="200px;" height="200px;"><p>上传文件,使用multipart/form-data类型</p><form action="/test/file" method="post" enctype="multipart/form-data"> <input type="file" name="file"> <button type="submit">上传</button></form><form action="/test/files" method="post" enctype="multipart/form-data"> <input type="file" name="files"> <input type="file" name="files"> <button type="submit">上传</button></form><p>==================================================================</p>private ResourceConfigBean resourceConfigBean;/** * 127.0.0.1/test/file ---- post *///@PostMapping(value = "/file", consumes = "multipart/form-data")(value = "/file", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)public String uploadFile( MultipartFile file, RedirectAttributes redirectAttributes) { if (file.isEmpty()) { redirectAttributes.addFlashAttribute("message", "Please select file"); return "redirect:/test/index"; } String absolutePath = resourceConfigBean.getLocalPathForWindow() + file.getOriginalFilename(); String relativePath = resourceConfigBean.getResourcePath() + file.getOriginalFilename(); try { File destFile = new File(absolutePath); file.transferTo(destFile); // 使用工具类 Files 来上传文件// byte[] bytes = file.getBytes();// Path path = Paths.get(absolutePath);// Files.write(path, bytes); } catch (IllegalStateException | IOException e) { e.printStackTrace(); redirectAttributes.addFlashAttribute("message", "Upload fail."); return "redirect:/test/index"; } redirectAttributes.addFlashAttribute("message", "Upload success."); redirectAttributes.addFlashAttribute("relativePath", relativePath); return "redirect:/test/index";}/** * 127.0.0.1/test/files ---- post */(value="/files", consumes="multipart/form-data")public String uploadFiles( MultipartFile[] files, RedirectAttributes redirectAttributes) { boolean isEmpty = true; try { for (MultipartFile file : files) { if (file.isEmpty()) { continue; } String fileName = file.getOriginalFilename(); String destFilePath = resourceConfigBean.getLocalPathForWindow() + fileName; File destFile = new File(destFilePath); file.transferTo(destFile); isEmpty = false; } } catch (IllegalStateException | IOException e) { e.printStackTrace(); redirectAttributes.addFlashAttribute("message", "Upload file failed."); return "redirect:/test/index"; } if (isEmpty) { redirectAttributes.addFlashAttribute("message", "Please select file."); } else { redirectAttributes.addFlashAttribute("message", "Upload file success."); } return "redirect:/test/index";}<p>==================================================================</p><h3>File Download</h3><p><a href="/test/file3?fileName=11.jpg">download file</a></p><p>==================================================================</p>/** * 127.0.0.1/test/file1?fileName=***.jpg ---- get */("/file1")public void downloadFile1(HttpServletRequest request, HttpServletResponse response, String fileName) { String filePath = resourceConfigBean.getLocalPathForWindow() + fileName; File downloadFile = new File(filePath); if (downloadFile.exists()) { response.setContentType("application/octet-stream"); response.setContentLength((int)downloadFile.length()); response.setHeader(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment; filename=\"%s\"", fileName)); byte[] buffer = new byte[1024]; FileInputStream fis = null; BufferedInputStream bis = null; try { fis = new FileInputStream(downloadFile); bis = new BufferedInputStream(fis); OutputStream os = response.getOutputStream(); int i = 0; while ((i = bis.read(buffer)) != -1) { os.write(buffer, 0, i); } } catch (Exception e) { LOGGER.debug(e.getMessage()); e.printStackTrace(); } finally { try { if (fis != null) { fis.close(); } if (bis != null) { bis.close(); } } catch (Exception e2) { LOGGER.debug(e2.getMessage()); e2.printStackTrace(); } } }}/** * 127.0.0.1/test/file2?fileName=***.jpg ---- get */("/file2")public void downloadFile2(HttpServletRequest request, HttpServletResponse response, String fileName) { String filePath = resourceConfigBean.getLocalPathForWindow() + fileName; File downloadFile = new File(filePath); try { if (downloadFile.exists()) { response.setContentType("application/octet-stream"); response.setContentLength((int)downloadFile.length()); response.setHeader(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment; filename=\"%s\"", fileName)); InputStream is = new FileInputStream(downloadFile); IOUtils.copy(is, response.getOutputStream()); response.flushBuffer(); } } catch (Exception e) { LOGGER.debug(e.getMessage()); e.printStackTrace(); }}/** * 127.0.0.1/test/file3?fileName=***.jpg ---- get */("/file3")public ResponseEntity<Resource> downLoadFile( String fileName) { try { Resource resource = new UrlResource(Paths.get( resourceConfigBean.getLocalPathForWindow() + fileName).toUri()); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_TYPE, "application/octet-stream") .header(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment; filename=\"%s\"", resource.getFilename())) .body(resource); } catch (Exception e) { e.printStackTrace(); } return null;}<!-- 国际化配置 --><!-- 设置国际化解析器,将 Locale 对象存储于 Session 中供后续使用 localeResolver 名字不能变更 --><bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"> <property name="defaultLocale" value="zh_CN" /></bean><!-- messageSource:配置国际化资源文件的路径 basenames | basename:classpath:i18n/language 下国际化文件 文件格式:***_语言_国家.properties:language_zh_CN.properties、language_en_US.properties useCodeAsDefaultMessage:true,当 Spring 在 ResourceBundle 中找不到 messageKey 的话, 不抛出 NoSuchMessageException,而是返回 messageKey --><bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <property name="defaultEncoding" value="UTF-8" /> <property name="useCodeAsDefaultMessage" value="true" /> <!-- <property name="basename" value="classpath:i18n/language" /> --> <property name="basenames"> <list> <value>classpath:i18n/language</value> </list> </property></bean><!-- 该拦截器通过请求参数 lang 来拦截 Http 请求,使其重新设置页面的区域化信息 --><mvc:interceptors> <bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"> <property name="paramName" value="lang" /> </bean></mvc:interceptors><% taglib uri="http://www.springframework.org/tags" prefix="spring"%>当前语言: ${pageContext.response.locale }<input type="text" class="form-control" name="userName" placeholder=<spring:message code="userName" />/><span class="bigger-110"><spring:message code="login" /></span>/** * 127.0.0.1/i18nLogin?lang=zh_CN * 127.0.0.1/i18nLogin?lang=en_US */("/i18nLogin")public String i18nLoginPage() { return "account/i18nLogin";}<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>3.3.1</version></plugin><dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-typehandlers-jsr310</artifactId> <version>1.0.2</version></dependency>
public class WebConfig extends WebMvcConfigurerAdapter{ public MappingJackson2HttpMessageConverter jacksonMessageConverter(){ MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter(); ObjectMapper mapper = new ObjectMapper(); //Registering Hibernate4Module to support lazy objects Hibernate4Module hm = new Hibernate4Module(); //Force Jackson serialize lazy objects hm.configure(Hibernate4Module.Feature.FORCE_LAZY_LOADING,true); mapper.registerModule(hm); messageConverter.setObjectMapper(mapper); return messageConverter; } public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { //Here we add our custom-configured HttpMessageConverter converters.add(jacksonMessageConverter()); super.configureMessageConverters(converters); }}