java基础和springboot

java基础

直接看Boogipop大佬的文章,贼全面 ,我就记录一些不会的

JVM 角度说进程与线程之间的关系

一个运行的exe就是一个进程,进程可以有多个线程

同一个进程中的多个线程共享堆和方法区

每个线程有独立的自己的程序计数器、虚拟机栈和本地方法栈

多线程并不能提高运行速度,只能提高CPU使用效率

JVM

在线程中执行一个方法时,会创建一个栈帧入栈并执行,不管该栈帧是正常结束还是异常,栈帧都会销毁

Scanner

SICTF看到一个很秀的操作

(#a=new java.util.Scanner(new java.io.File("/flag")).next())

//“#a”是因为使用OGNL表达式

读取内容

两种方式读取next(),nextline()

next()空格为分隔符,nextline()回车为分隔符

Scanner test = new Scanner(System.in);
if(test.hasNext()){
	String a = test.next();
	System.out.println(a);
}

验证输入有hasNextInthasNextDouble等,然后使用对应的nextIntnextDouble等接收参数

命令行传参

是在src目录下运行,所以加上路径

java com.boogipop.www.base.DemoReload i am boogipop

image-20230911213326053

可变参数

简单来说就是函数的参数数量不限

(参数类型 ... 变量名) (加三个点)

package org.example.basic;

public class test {
    public static void main(String[] args) {
        print(1,2,3,4,5,6,78,32,32,432,423,423,423);
    }
    public static void print(double ... number){
        if (number.length==0){
            System.out.println("no args");
            return;
        }
        else {
            for(int i=0;i<=number.length;i++){
                System.out.println(number[i]);
            }
        }
    }
}

数组

定义一个数组,int[] num = {1,2,3,4,5,6}

然后输入num.for

就会出来增强for循环

for (int i : num) {
    
}

这里的i相当于python中的for i in a,直接就是数组内容

image-20230911221347848

Arrays

Arrays.toString()输出数组内容

Arrays.sort()给数组排序

Arrays.fill()特定值填充数据

int[] nm = {12,2,34,15,06};
System.out.println(nm);
System.out.println(Arrays.toString(nm));
Arrays.sort(nm);
System.out.println(Arrays.toString(nm));
Arrays.fill(nm,2);
System.out.println(Arrays.toString(nm));
Arrays.fill(nm,2,4,0);     #数组下标2-4,左闭右开,填充0
System.out.println(Arrays.toString(nm));
        
##
[I@677327b6  #数组地址
[12, 2, 34, 15, 6]
[2, 6, 12, 15, 34]
[2, 2, 2, 2, 2]
[2, 2, 0, 0, 2]

构造函数、this、this()、super、super()、this动态绑定

this :代表“当前对象”

this():就是调用“当前对象”的无参构造方法
this、this(),new的谁就指向谁。父类里有this也是指向的new出来的对象。

this的动态绑定:包括静态方法和静态成员变量是前期绑定、访问方法是后期绑定

super:指向父类

super():就是调用父类的无参构造方法
super、super(),仅指向上一级父类。

image-20230911224602382

A为父类,当前对象就是new Bbbs3()super.s2()即调用父类的s2()Athis.s1()调用当前对象的s1(),即bbs1(),也就是B中的s1()

所以输出B中的s1

A不加this的话也是输出B中的s1,因为B为子类,相当于重写了s1方法

  • 调用方法时this是指向当前对象,但是调用属性时,是指的当前类的属性

两个案例理解一下这句话

image-20230911225731256

image-20230911225746289

instanceof

判断两个类之间有无父子关系

image-20230911232058359

多态

image-20230912102708510

静态代码块、匿名代码块、构造代码块

抽象类

类似占位符,只能定义不能写

接口

跟抽象类一样

内部类

套娃

捕获异常

try catch finally throw throws

finally不管怎样都会执行

java io

字节流

FileOutputStream

FileOutputStream out = new FileOutputStream("flag"); //创建flag文件
String hello = "Hello World!";
out.write(97);
out.write('a');
out.write(hello.getBytes());    //转为字节数组
out.close();
System.out.println("finish");

FileInputStream

FileInputStream in = new FileInputStream("flag");
while ((read=in.read())!=-1) {      
    System.out.printf(read+",");
    System.out.printf((char)read+",");
}
in.close();

每经过一次in.read()就会指向下一个内容,所以先把值赋给一个变量

in.read()返回int类型

FileInputStream in = new FileInputStream("flag");
byte[] buf = new byte[3]; //
System.out.println(in.read(buf)); //把内容读入数组(缓冲区),输出的是数组长度
System.out.println(new String(buf,0,1)); 
//0为偏移,1为读取的长度,不能超过数组的长度
in.close();

BufferedInputStream

读入缓冲区,作用跟byte[] buf = new byte[3];一样

image-20230912112835162

FileInputStream in = new FileInputStream("flag");
BufferedInputStream bif = new BufferedInputStream(in);
while ((read=bif.read())!=-1){
    System.out.print((char)read);
}

bif.close(); //也会关闭in

BufferedOutputStream

FileOutputStream out=new FileOutputStream("1.txt");
BufferedOutputStream bos=new BufferedOutputStream(out);
for(int i=0;i<=10;i++){
     bos.write("helloworld,".getBytes()); //到这一步不会写入文件,只是写入了缓冲区
     bos.flush();//刷新缓冲区写入文件
}
bos.close();//关闭的时候也会调用flush,同时关闭out

ObjectInputStream和ObjectOutputStream

序列化与反序列化

重写方法

private void writeObject(ObjectOutputStream s)throws java.io.IOException


private void readObject(ObjectInputStream oos)throws java.io.IOException, ClassNotFoundException
{
	oos.defaultReadObject();
	
}

字符流

使用字节流读取中文会出现乱码

FileReader

FileReader fr=new FileReader("resource/test.txt");
char[] buf=new char[1024]; //创建一个缓冲区
int count=0;
while((count=fr.read(buf))!=-1){
    System.out.println(new String(buf,0,count));

FileWriter

FileWriter fw=new FileWriter("writer.txt");
//写入
for(int i=0;i<10;i++){
    fw.write("java is the best");
    fw.flush();
}
fw.close();

BufferReader

转换流

InputStreamReader/OutputStreamWriter

  • 可将字节流变为转换流
  • 可设置字符的编码方式
FileInputStream fs= new FileInputStream("resource/test.txt");
 InputStreamReader isr=new InputStreamReader(fs,"utf-8");//传入字节流,转换流指定utf-8编码
 //读取文件
 int data=0;
 while((data=isr.read())!=-1){
     System.out.println((char)data);
 }
 isr.close();
FileOutputStream fo=new FileOutputStream("fo.txt");
OutputStreamWriter osw=new OutputStreamWriter(fo,"GBK"); //gbk编码写入
for(int i=1;i<=10;i++){
    osw.write("我是傻逼");
    osw.flush();
}
osw.close();

Object类

equals()

== 基础类型对比的是值是否相同,引用类型对比的是引用是否相同;
equals只是单单的比较值

        String s1 = "string";
        String s2 = "string";
        String s3 = new String("string");
        System.out.println(s1==s2);
        System.out.println(s1==s3);
        System.out.println(s1.equals(s2));
        System.out.println(s1.equals(s3));
        
/*
true
false
true
true
*/

String类

str.charAt(str.length()-1) //里面的数字相当于数组下标

//charAt();返回对应下标的字符
//toCharArray();返回字符串对应数组
//indexOf('asd');返回字符串首次出现位置
//lastIndexOf('asd');返回字符串最后一次出现的位置

集合

Collection接口

Collection col = new Arraylist();
col.add(s1);
col.add(s2);
col.add(s3);
Iterator it = col.iterator();  //迭代器
while(it.hasNext()){
	System.out.println(it.next());
}

泛型

实际上就是常见的

Map集合

public class test {
    public static void main(String[] args) throws IOException {
        Map<String,String> map = new HashMap<String, String>();
        map.put("cn","中国");
        map.put("usa","美国");
        map.put("uk","英国");
        //遍历map
        Set<String> keyset = map.keySet();
        for (String s : map.keySet()) {
            System.out.println(s+"--------"+map.get(s));
        }
        System.out.println("----------entryset---------");
        Set<Map.Entry<String,String>> entries = map.entrySet();
        for (Map.Entry<String, String> entry : entries) {
            System.out.println(entry.getKey()+"------"+entry.getValue());
        }
    }
}

WOW,entryset,跟CC1的有一步骤有关

注解与反射

Class.forName(className, true, currentLoader)

第⼆个参数表示是否初始化,在 forName 的时候,构造函数并不会执⾏,而是执⾏类初始化。他会执行static{}静态块里面的内容

newInstance() 对类进行实例化

但需要注意的是,invoke 方法第一个参数并不是固定的:

如果调用这个方法是普通方法,第一个参数就是类对象;

如果调用这个方法是静态方法,第一个参数就是类;

如果调用的方法是静态方法。那么invoke方法传入的第一个参数永远为null

动态代理

newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
  • loader – 类加载器来定义代理类
  • interfaces – 代理类实现的接口列表
  • h – 调度方法调用的调用处理函数
Hello hello = (Hello) Proxy.newProxyInstance(
            Hello.class.getClassLoader(), // 传入ClassLoader
            new Class[] { Hello.class }, // 传入要实现的接口
            handler); // 传入处理调用方法的InvocationHandler
Proxy.newProxyInstance(ordinaryStudents.getClass().getClassLoader(), ordinaryStudents.getClass().getInterfaces(), handler)

通常是这两句,参数调整一下

InvocationHandler instance = (InvocationHandler) constructor.newInstance(Target.class,lazymap);
Map proxyInstance = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},instance);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");

Constructor constructor = c.getDeclaredConstructor(Class.class,Map.class);

constructor.setAccessible(true);
InvocationHandler instance = (InvocationHandler) constructor.newInstance(Target.class,lazymap);
Map proxyInstance = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},instance);
Object o = constructor.newInstance(Override.class,proxyInstance);

类加载

BootstrapClassLoader ExtensionsClassLoader AppClassLoader

URLClassLoader

①:URL未以斜杠 / 结尾,则认为是一个JAR文件,使用 JarLoader 来寻找类,即为在Jar包中寻找.class文件

②:URL以斜杠 / 结尾,且协议名是 file ,则使用 FileLoader 来寻找类,即为在本地文件系统中寻找.class文件

③:URL以斜杠 / 结尾,且协议名不是 file ,则使用最基础的 Loader 来寻找类。

package com.ctf.bcel;

import java.io.IOException;

public class calc {
    static{
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

file:// 路径为.class文件所在路径

public class BCELDemo {
    public static void main(String[] args) throws Exception {
        URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("file://C:\\Users\\86136\\Desktop\\rmi\\out\\production\\rmi\\com\\ctf\\bcel")});
        urlClassLoader.loadClass("com.ctf.bcel.calc").newInstance();
    }     //包名.类名
}

http:// python起一个http服务

public class BCELDemo {
    public static void main(String[] args) throws Exception {
        URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("http://127.0.0.1:8000")});
        urlClassLoader.loadClass("com.ctf.bcel.calc").newInstance();
    }     //包名.类名
}

将我们之前的 class 文件打包一下,打包为 jar 文件。

jar -cvf calc.jar clac.class

file + jar协议

public class BCELDemo {
    public static void main(String[] args) throws Exception {
        URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("jar:file:///E:\\Calc.jar!/")});
        urlClassLoader.loadClass("com.ctf.bcel.calc").newInstance();
    }     //包名.类名
}

http + jar协议

public class BCELDemo {
    public static void main(String[] args) throws Exception {
        URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("jar:http://127.0.0.1:9999/Calc.jar!/")});
        urlClassLoader.loadClass("com.ctf.bcel.calc").newInstance();
    }     //包名.类名
}

加载字节码

ClassLoader#defineClass

defineClass(String name,byte[] b,int off,int len)
//name为类名,b为字节码数组,off为偏移量,len为字节码数组的长度。
public class BCELDemo {
    public static void main(String[] args) throws Exception {
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        Method method = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
        method.setAccessible(true);
        byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\86136\\Desktop\\rmi\\out\\production\\rmi\\com\\ctf\\bcel\\calc.class"));
        Class c = (Class) method.invoke(systemClassLoader,"com.ctf.bcel.calc",code,0,code.length);
        c.newInstance();

    }
}

Unsafe 的defineClass

public class BCELDemo {
    public static void main(String[] args) throws Exception {
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
        Class<Unsafe> unsafeClass = Unsafe.class;
        Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
        unsafeField.setAccessible(true);
        Unsafe classUnsafe = (Unsafe) unsafeField.get(null);
        Method defineClassMethod = unsafeClass.getMethod("defineClass", String.class, byte[].class,
                int.class, int.class, ClassLoader.class, ProtectionDomain.class);
        byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\86136\\Desktop\\rmi\\out\\production\\rmi\\com\\ctf\\bcel\\calc.class"));
        Class calc = (Class) defineClassMethod.invoke(classUnsafe, "com.ctf.bcel.calc", code, 0, code.length, classLoader, null);
        calc.newInstance();

    }
}

TemplatesImpl 加载字节码 !!!!

TemplatesImpl#getOutputProperties() -> TemplatesImpl#newTransformer() ->

TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses()

-> TransletClassLoader#defineClass()

TemplatesImpl#getOutputProperties() TemplatesImpl#newTransformer()

可以利用这两个去构造poc

首先先构造字节码,注意,这里的字节码必须继承AbstractTranslet,因为继承了这一抽象类,所以必须要重写一下里面的方法

package com.ctf.bcel;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import java.io.IOException;

public class calc extends AbstractTranslet {
    public calc() throws IOException {
        super();
        Runtime.getRuntime().exec("calc");
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}
public class TemplatesRce {  
    public static void main(String[] args) throws Exception{  
        byte[] code = Files.readAllBytes(Paths.get("E:\\JavaClass\\TemplatesBytes.class"));  
 TemplatesImpl templates = new TemplatesImpl();  
 setFieldValue(templates, "_name", "Calc");  
 setFieldValue(templates, "_bytecodes", new byte[][] {code});  
 setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());  
 templates.newTransformer();  
 }  
    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{  
        Field field = obj.getClass().getDeclaredField(fieldName);  
 field.setAccessible(true);  
 field.set(obj, value);  
 }  
}

BCEL 加载字节码

通过 BCEL 提供的两个类 RepositoryUtility 来利用: Repository 用于将一个Java Class 先转换成原生字节码,当然这里也可以直接使用javac命令来编译 java 文件生成字节码; Utility 用于将原生的字节码转换成BCEL格式的字节码:

JavaClass cls = Repository.lookupClass(Evil.class);
String code = Utility.encode(cls.getBytes(), true);
System.out.println("$$BCEL$$"+code);

new ClassLoader().loadClass("$$BCEL$$" + code).newInstance();
import java.io.IOException;  
   
public class Calc {  
    static {  
        try {  
            Runtime.getRuntime().exec("calc");  
 		} catch (IOException e){  
            e.printStackTrace();  
 		}  
    }  
}

读p神文章

编译.xsl生成.class

com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl,它在defineClass中需要的字节码所对应的基类,就是这里的com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet

XSLT在使用时会先编译成Java字节码,这也就是为什么TemplatesImpl会使用defineClass的原因

springboot

创建程序

首先安装好maven,我修改了maven的repository的位置,所以要在settings.xml中修改,修改完后关键是mvn help system下载repository里的文件(自行上网查吧)

image-20230913161000684

image-20230913160546330

image-20230913160804466

高版本springboot需要java的高版本,我这java8用的是2.2.0(生成后手动改的)

只选择spring web就够了

image-20230913161643123

算了,不太会讲,就记录知识吧

@Configuration    //标明这是配置类
@Bean     //给容器中添加组件
@Component   //注册
@Controller@Service@Repository
@ComponentScan@Import
@ConditionalOnxxxxx满足Conditional指定的条件,则进行组件注入
@ImportResource("classpath:beans.xml")
@ConfigurationProperties(prefix="")    //能够在配置文件中赋值
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)


SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration

每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。xxxxProperties里面拿。xxxProperties和配置文件进行了绑定

生效的配置类就会给容器中装配很多组件
设置静态资源访问目录

spring:
  mvc:
    static-path-pattern: /res/**

启动程序主要是这三个东西@SpringBootApplicationSpringApplicationrun

充当配置文件的application.properties也可以是application.yml

在启动程序Application的同级目录下创建controller文件夹,在这写有关路由的东西

@Controller (返回页面) @RestController(返回内容)

application.properties中能配置端口server.port=8081

自动装配原理

yaml

大小写敏感
使用缩进表示层级关系
缩进不允许使用tab,只允许空格
缩进的空格数不重要,只要同级元素左对齐即可
#表示注释
Person:
  name: zero${random.uuid}}
  age: ${random.int}
  birth: 2018/11/02
  dog:
    name: wow
    age: 12
server:
  port: 8082

dog:
  name: pop
  age: 321

#Human: ["myname","myage","nothing"]

#Hulman: ["myname","youage","everything",[1,2,3]]
Hulman:
  - myname
  - youage
  - everything
  -
    - 1
    - 2
    - 3
      
#companies: [{id: 1,name: asd},{id: 2,name: sdf}]
companies:
  -
    id: 1
    name: asd
  -
    id: 2
    name: sdf

boolean:
  - true
  - false
float:
  - 3.14
  - 1.14
int:
  - 1
  - 2

赋值

使用Spring的@Value注解进行赋值,并且配合@Component注解注册bean

image-20230913163225847

yaml配置文件赋值

@ConfigurationProperties(prefix = "dog")

image-20230913163400091

dog:
  name: pop  
  age: 321
#(注意空格)

properties配置文件

@PropertySource("classpath:user.properties")

松散绑定

yml中写last-namelastName是一样的,-后面跟着的字母默认是大写的

JSR303校验

@Validated

   @Email()
   private String name;

一般使用Pattern() 正则匹配

image-20230913164032098

image-20230913164049132

多个配置文件位置

默认选择顺序

image-20230913164744829

主动选择激活配置 spring.profiles.active=

application-名字.properties

image-20230913165341467

选择application-test.properties的配置

#application.yml
spring:
  profiles:
    active: dev   //选择执行名字为dev的配置
server:
  port: 8081

#(---不是为了好看画的、起到分隔的作用)
---
server:
  port: 8082     
spring:
  profiles: dev   //名字
---
server:
  port: 8083
spring:
  profiles: test

自动装配

//标注这是一个配置类
@Configuration()   
@EnableConfigurationProperties()

//根据不同的条件来判断当前配置是否生效
@ConditionalOnWebApplication()
@ConditionalOnClass()
@ConditionalOnProperty()

xxxxAutoConfiguration:自动配置类;给容器中添加组件

xxxxProperties:封装配置文件中相关属性

而相关属性我们可以使用配置文件进行修改

web开发

webjars

获取静态资源resources下放public、static、resources

优先级 resources > static > public

首页:在三个目录下寻找index.html

图标:2.2版本没有,2.1版本放在三个任意一个目录下favicon.ico

@Controller .html文件放在template目录下

thymeleaf 模板

*{…}也可和${…}混用,星号语法对选定对象而不是整个上下文评估表达式,也就是说,只要没有选定的对象,美元(${…})和星号(*{...})的语法就完全一样。

<div th:object="${user}">
    <p>Name: <span th:text="*{name}"></span>.</p>
    <p>Age: <span th:text="*{age}">18</span>.</p>
    <p>Detail: <span th:text="*{detail}">好好学习</span>.</p>
</div>

//等价于
<div >
    <p>Name: <span th:text="*{user.name}"></span>.</p>
    <p>Age: <span th:text="${user.age}">18</span>.</p>
    <p>Detail: <span th:text="${user.detail}">好好学习</span>.</p>
</div>

th:utext=”${msg}” <h1>123</h1>能够被成功解析

${对象.get方法名}

th:each=”user:${users}” th:text=”${user}”

MVC配置

@Configuration

public class MyMvcConfig implements WebMvcConfigurer {
    @Bean
    public ViewResolver myview(){
        return new MyViewResolver();
    }
    public static class MyViewResolver implements ViewResolver{
        @Override
        public View resolveViewName(String viewName, Locale locale) throws Exception {
            return null;
        }
    }
}

员工管理系统

所有页面的静态资源都需要使用thymeleaf接管()

标上xmlns:th=”http://www.thymeleaf.org“ ,以及修改href和src….等本地链接

th:href th:src th:link @{/}

国际化 #{}

即页面的中英文转化

先创建login.properties,再创建login_zh_CN.properties

image-20230914183413701

然后设置信息

设置完后spring.messages.basename=i18n.login

然后在html中对应的显示位置 添加 th:xx=”#{}”

th:href="@{/index.html(l=zh_CN)}"
th:href="@{/index.html(l=en_US)}"
点击按钮转换中英文,设置组件LocaleResolver

public class myLocaleResolver implements LocaleResolver {
    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        String language = request.getParameter("l");
        Locale locale = Locale.getDefault();

        if (!StringUtils.isEmpty(language)){
            //zh_CN
            String[] s = language.split("_");
            //语言,国家
            locale = new Locale(s[0],s[1]);
        }
        return locale;
    }

    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {

    }
}

然后在自定义的myMvcConfig中放入@Bean中

@Bean
public LocaleResolver localeResolver(){
    return new myLocaleResolver();
}

登录

@Controller
public class loginController {
    @RequestMapping("/login")
    public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model){
        if (!StringUtils.isEmpty(username) && "admin".equals(password)){
            return "index";
            //先在mvc中添加映射,然后跳转
            //return "redirect:/main.html"; 
        }
        else {
        	//提示信息
            model.addAttribute("msg","用户名或密码错误");
            return "login";
        }
    }
}
<p th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>

拦截器

public class loginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    	//获取session
        Object loginuser = request.getSession().getAttribute("loginuser");
        if (loginuser==null){
            request.setAttribute("msg","请先登录");
          request.getRequestDispatcher("/login.html").forward(request,response);
          //跳转
            return false;
        }else {
            return true;
        }
    }
}

mvc中添加

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new loginInterceptor()).addPathPatterns("/**").excludePathPatterns("/","/login.html","/about.html","/contact.html","/js/**","/css/*","/img/*");
}
@RequestMapping("/login.html")
public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model, HttpSession session){
    if (!StringUtils.isEmpty(username) && "admin".equals(password)){
        session.setAttribute("loginuser",username);

        return "redirect:index.html";

员工列表

提取公共页面

把相同的代码添加一个属性 th:fragment=”sidebar”

调用 th:replace=”~{commons/commons::topbar}” //定义属性的位置

高亮

()传递参数 ~{commons/commons::topbar(active=”index.html”)}

th:xx="${active=='index.html'?'nav-link active':'nav-link'}" 三元运算符判断是否高亮

展示员工界面

<tr th:each="user:${users}">
	<td th:text="${user.getId()}"></td>
	<td th:text="${user.getName()}"></td>
	<td th:text="${user.getAge()}"></td>
	<td th:text="${user.getId()}"></td>
	<td th:text="${user.getId()}"></td>

</tr>


java基础和springboot
https://zer0peach.github.io/2023/09/11/java基础和springboot/
作者
Zer0peach
发布于
2023年9月11日
许可协议